lib/ropes: use `redef` for inherited named constructors
[nit.git] / lib / standard / ropes.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # This file is free software, which comes along with NIT. This software is
4 # distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
5 # without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
6 # PARTICULAR PURPOSE. You can modify it if you want, provided this header
7 # is kept unaltered, and a notification of the changes is added.
8 # You are allowed to redistribute it and sell it, alone or as a part of
9 # another product.
10
11 # Nit implementation of the Ropes (see Ropes : An Alternative to Strings,
12 # SOFTWARE - PRACTICE AND EXPERIENCE, VOL. 25(12), 1315 - 1330 (DECEMBER 1995)
13 # Hans. J Boehm, Russ Atkinson and Michael Plass)
14 #
15 # A rope is a kind of string but instead of being flat, it relies on a binary tree structure to store data.
16 module ropes
17
18 import file
19 intrude import string
20
21 # Structure, tuple containing a LeafNode and an Int
22 # Used for methods like [] or has/has_only
23 private class TupleLeafNodePos
24 private var curr_node: LeafNode
25 private var corrected_pos: Int
26 private var visit_stack: List[TupleVisitNode]
27 end
28
29 # Abstract class, represents all the services offered by both mutable and immutable ropes
30 abstract class Rope
31 super Comparable
32 super StringCapable
33
34 # Cached version of self as a flat String
35 private var str_representation: nullable String = null
36
37 redef type OTHER: Rope
38
39 # The first node of the hierarchy
40 private var parent_node: RopeNode
41
42 # Needed by the compiler to avoid producing an error with constructors in subclasses
43 init do
44 self.parent_node = new ConcatNode
45 end
46
47 # Initializes a new Rope with a text embedded in directly
48 init with_string(str: String) do
49 self.parent_node = new ConcatNode
50 parent_node.as(ConcatNode).right_child = new LeafNode(str)
51 parent_node.as(ConcatNode).update_data
52 end
53
54 # Returns a view on the rope
55 fun chars: SequenceRead[Char]
56 do
57 return new CharRopeView(self)
58 end
59
60 # Gets the total length of the Rope
61 fun length: Int
62 do
63 return parent_node.length
64 end
65
66 # Returns a flat version of self
67 redef fun to_s
68 do
69 if self.str_representation == null then flatten
70 return str_representation.as(not null)
71 end
72
73 # Stores a flat version of self in cache
74 private fun flatten: FlatString
75 do
76 var native_final_str = calloc_string(length + 1)
77
78 native_final_str[length] = '\0'
79
80 var offset = 0
81
82 var iter = new DFSRopeLeafIterator(self)
83
84 while iter.is_ok do
85 iter.item.value.as(FlatString).items.copy_to(native_final_str, iter.item.value.length, 0, offset)
86 offset += iter.item.value.length
87 iter.next
88 end
89
90 return native_final_str.to_s_with_length(length)
91 end
92
93 # Gets a node containing the substring to seek the char at the require position
94 private fun get_node_for_pos(position: Int): TupleLeafNodePos
95 do
96 assert position >= 0 and position < self.length
97
98 var curr_node: nullable RopeNode = parent_node
99
100 var visit_stack = new List[TupleVisitNode]
101
102 var curr_visit_tuple: TupleVisitNode
103
104 loop
105 if curr_node isa ConcatNode then
106 curr_visit_tuple = new TupleVisitNode(curr_node)
107 if curr_node.left_child != null and position < curr_node.left_child.length then
108 curr_visit_tuple.left_visited = true
109 curr_node = curr_node.left_child
110 else if curr_node.right_child != null then
111 curr_visit_tuple.left_visited = true
112 curr_visit_tuple.right_visited = true
113 if curr_node.left_child != null then position -= curr_node.left_child.length
114 curr_node = curr_node.right_child
115 else
116 print "Fatal Error"
117 abort
118 end
119 visit_stack.push(curr_visit_tuple)
120 else if curr_node isa LeafNode then
121 return new TupleLeafNodePos(curr_node, position, visit_stack)
122 end
123 end
124 end
125
126 # Concats two ropes and returns a new one
127 fun +(other: Rope): Rope do
128 var new_rope = new BufferRope
129
130 var first_iter = new DFSRopeLeafIterator(self)
131
132 while first_iter.is_ok do
133 new_rope.append(first_iter.item.value)
134 first_iter.next
135 end
136
137 var second_iter = new DFSRopeLeafIterator(other)
138
139 while second_iter.is_ok do
140 new_rope.append(second_iter.item.value)
141 second_iter.next
142 end
143
144 return new_rope
145 end
146
147 # Returns a copy of several ropes concatenated
148 #
149 # Is equivalent to a chain of + operations
150 # Except this one is optimized for performance
151 fun multi_concat(ropes: Rope...): Rope
152 do
153 var new_rope = new BufferRope
154
155 var self_iter = self.iterator
156 while self_iter.is_ok do
157 new_rope.append(self_iter.item.value)
158 self_iter.next
159 end
160
161 for i in ropes do
162 var iter = i.iterator
163 while iter.is_ok do
164 new_rope.append(iter.item.value)
165 iter.next
166 end
167 end
168
169 return new_rope
170 end
171
172 # Appends the content of self multiple times in a new Rope object
173 fun *(repeats: Int): Rope do
174
175 var new_rope = new BufferRope
176
177 var str = self.to_s
178
179 for i in [1 .. repeats] do new_rope.append(str)
180
181 return new_rope
182 end
183
184 # Returns an iterator on self
185 #
186 # Unsafe modifications on a MutableRope
187 #
188 private fun iterator: Iterator[LeafNode] do return new DFSRopeLeafIterator(self)
189
190 # Creates a subrope.
191 #
192 # var rope = (new BufferRope).append("abcd")
193 #
194 # assert rope.subrope(1, 2) == "bc"
195 # assert rope.subrope(-1, ) == "a"
196 # assert rope.subrope(1, 0) == ""
197 # assert rope.subrope(2, 5) == "cd"
198 #
199 # A `index_from` index < 0 will be replaced by 0.
200 # Unless a `count` value is > 0 at the same time.
201 # In this case, `index_from += count` and `count -= index_from`.
202 #
203 fun subrope(index_from: Int, count: Int): Rope
204 do
205 assert count >= 0
206
207 if index_from < 0 then
208 count += index_from
209 if count < 0 then count = 0
210 index_from = 0
211 end
212
213 if count - index_from >= self.length then count = length - index_from
214
215 var buffer = new BufferRope
216
217 var iter = new DFSRopeLeafIterator.with_index(self, index_from)
218
219 var curr_subrope_index = index_from - iter.pos
220
221 while iter.is_ok do
222 if count == 0 then break
223 if curr_subrope_index > 0 then
224 if count >= iter.item.value.length then
225 buffer.append(iter.item.value.substring(curr_subrope_index, count))
226 count -= iter.item.value.length - curr_subrope_index
227 curr_subrope_index = 0
228 else
229 buffer.append(iter.item.value.substring(curr_subrope_index, count))
230 break
231 end
232 else
233 if count >= iter.item.value.length then
234 buffer.append(iter.item.value)
235 count -= iter.item.value.length
236 else
237 buffer.append(iter.item.value.substring(0, count))
238 break
239 end
240 end
241
242 iter.next
243 end
244
245 return buffer
246 end
247
248 # Returns an upper (capitalized) version of self
249 fun to_upper: Rope
250 do
251 var new_rope = new BufferRope
252 var iter = new DFSRopeLeafIterator(self)
253 while iter.is_ok do
254 new_rope.append(iter.item.value.to_upper)
255 iter.next
256 end
257 return new_rope
258 end
259
260 # Returns a lower (minuscule) version of self
261 fun to_lower: Rope
262 do
263 var new_rope = new BufferRope
264 var iter = new DFSRopeLeafIterator(self)
265 while iter.is_ok do
266 new_rope.append(iter.item.value.to_lower)
267 iter.next
268 end
269 return new_rope
270 end
271
272 ############################################################################
273 # Comparable Refined Methods #
274 ############################################################################
275
276 # Compares the current Rope to another object (either another rope or a String)
277 redef fun == (other)
278 do
279 if other == null or not (other isa Rope or other isa FlatText) then return false
280 var self_iter = new RopeCharIterator(self)
281 if other isa Rope then
282 if self.length != other.length then return false
283 var other_iterator = new RopeCharIterator(other)
284 while self_iter.is_ok do
285 if self_iter.item != other_iterator.item then return false
286 self_iter.next
287 other_iterator.next
288 end
289 else if other isa FlatText then
290 var pos = 0
291 if self.length != other.length then return false
292 while self_iter.is_ok do
293 if self_iter.item != other[pos] then return false
294 pos += 1
295 self_iter.next
296 end
297 end
298 return true
299 end
300
301 # Checks if self is lesser than other
302 #
303 # Comparison done in lexicographical order
304 # i.e. 'aa' < 'b'
305 #
306 redef fun <(other)
307 do
308 var other_iter = other.chars.iterator
309 for i in self.chars do
310 if not other_iter.is_ok then return false
311 if i < other_iter.item then return true
312 if i > other_iter.item then return false
313 other_iter.next
314 end
315 if other_iter.is_ok then return true
316 return false
317 end
318
319 end
320
321 # Rope that can be modified
322 #
323 # /!\ Non Thread-safe /!\
324 #
325 class BufferRope
326 super Rope
327
328 var is_dirty: Bool = false
329
330 init
331 do
332 super
333 end
334
335 redef init with_string(str)
336 do
337 super
338 end
339
340 ############################################################################
341 # Tree Balancing Methods #
342 ############################################################################
343
344 # Performs a right rotation on a node of the Rope
345 #
346 # Root Pivot
347 # / \ / \
348 # Pivot Leaf3 => Leaf1 Root
349 # / \ / \
350 # Leaf1 Leaf2 Leaf2 Leaf3
351 private fun rotate_right(root: ConcatNode)
352 do
353 assert root.left_child != null
354 var pivot = root.left_child.as(ConcatNode)
355 var root_new_left = pivot.right_child
356 var root_parent = root.parent
357
358 root.left_child = root_new_left
359 pivot.right_child = root
360 if root_parent == null then
361 self.parent_node = pivot
362 pivot.parent = null
363 return
364 end
365
366 if root_parent.left_child == root then
367 root_parent.left_child = pivot
368 else
369 root_parent.right_child = pivot
370 end
371
372 root.update_data
373 pivot.update_data
374 root_parent.update_data
375 end
376
377 # Performs a left rotation on a node of the Rope
378 #
379 # Root Pivot
380 # / \ / \
381 # Leaf1 Pivot => Root Leaf3
382 # / \ / \
383 # Leaf2 Leaf3 Leaf1 Leaf2
384 private fun rotate_left(root: ConcatNode)
385 do
386 assert root.right_child != null
387 var pivot = root.right_child.as(ConcatNode)
388 var root_new_right = pivot.left_child
389 var root_parent = root.parent
390
391 root.right_child = root_new_right
392 pivot.left_child = root
393 if root_parent == null then
394 self.parent_node = pivot
395 pivot.parent = null
396 return
397 end
398
399 if root_parent.left_child == root then
400 root_parent.left_child = pivot
401 else
402 root_parent.right_child = pivot
403 end
404
405 root.update_data
406 pivot.update_data
407 root_parent.update_data
408 end
409
410 # Shortcut method to balance a node and its parents
411 # based on the rules of the AVL Tree
412 private fun balance_from_node(parent_node: nullable ConcatNode)
413 do
414 while parent_node != null do
415 parent_node.update_data
416 var node_balance = parent_node.balance_factor
417 if node_balance < -1 or node_balance > 1 then
418 balance_node(parent_node)
419 end
420 parent_node = parent_node.parent
421 end
422 end
423
424 # Performs rotations to balance a node according to AVL Tree rules
425 private fun balance_node(node: ConcatNode)
426 do
427 var balance_factor = node.balance_factor
428 if balance_factor < -1 then
429 var right_balance = node.right_child.balance_factor
430 if right_balance < 0 then
431 rotate_left(node)
432 else
433 rotate_right(node.right_child.as(ConcatNode))
434 rotate_left(node)
435 end
436 else
437 var left_balance = node.left_child.balance_factor
438 if left_balance > 0 then
439 rotate_right(node)
440 else
441 rotate_left(node.left_child.as(ConcatNode))
442 rotate_right(node)
443 end
444 end
445 end
446
447 ############################################################################
448 # BufferRope exclusive Methods #
449 ############################################################################
450
451 # Appends a new Collection[Char] at the end of the current rope
452 fun append(str: String): BufferRope
453 do
454 var last_node = parent_node
455
456 while last_node isa ConcatNode and last_node.right_child != null do
457 last_node = last_node.right_child.as(not null)
458 end
459
460 if last_node isa ConcatNode then
461 last_node.right_child = new LeafNode(str.to_s)
462 else if last_node isa LeafNode then
463 var last_node_parent = last_node.parent
464 var new_concat = new ConcatNode
465 last_node_parent.right_child = new_concat
466 new_concat.left_child = last_node
467 new_concat.right_child = new LeafNode(str.to_s)
468 last_node = new_concat
469 else
470 print "Fatal Error, please report to the developers for more insight."
471 abort
472 end
473
474 balance_from_node(last_node)
475
476 is_dirty = true
477
478 return self
479 end
480
481 # Variatic function to append several collections of Chars
482 fun append_multi(strs: String...): BufferRope
483 do
484 for i in strs do
485 append(i)
486 end
487 return self
488 end
489
490 # Adds a new Collection[Char] at the beginning of the rope
491 fun prepend(str: String): BufferRope
492 do
493 var curr_node = parent_node
494
495 while curr_node isa ConcatNode and curr_node.left_child != null do
496 curr_node = curr_node.left_child.as(not null)
497 end
498
499 if curr_node isa ConcatNode then
500 curr_node.left_child = new LeafNode(str.to_s)
501 else if curr_node isa LeafNode then
502 var parent = curr_node.parent
503 var new_concat = new ConcatNode
504 var new_leaf = new LeafNode(str.to_s)
505 new_concat.left_child = new_leaf
506 new_concat.right_child = curr_node
507 parent.left_child = new_concat
508 curr_node = new_concat
509 else
510 print "Fatal Error"
511 abort
512 end
513
514 balance_from_node(curr_node)
515
516 is_dirty = true
517
518 return self
519 end
520
521 # Variatic function to prepend several collections of Chars
522 fun prepend_multi(strs: String...): BufferRope
523 do
524 for i in strs do
525 prepend(i)
526 end
527 return self
528 end
529
530 # Adds the content of `str` after self, does not create a new rope object
531 fun concat(str: Rope): Rope
532 do
533 var other_iter = new DFSRopeLeafIterator(str)
534
535 var modif_list = new List[String]
536
537 while other_iter.is_ok do
538 modif_list.push(other_iter.item.value)
539 other_iter.next
540 end
541
542 while modif_list.length > 0 do
543 self.append(modif_list.shift)
544 end
545
546 if not is_dirty then is_dirty = true
547
548 return self
549 end
550
551 # Returns the content of the current BufferRope object as an ImmutableRope
552 fun freeze: ImmutableRope
553 do
554 var buffer_rope = new BufferRope
555 var new_rope = new ImmutableRope
556
557 var iter = new DFSRopeLeafIterator(self)
558
559 while iter.is_ok do
560 buffer_rope.append(iter.item.value)
561 iter.next
562 end
563
564 new_rope.parent_node = buffer_rope.parent_node
565
566 if not is_dirty then new_rope.str_representation = self.str_representation
567
568 return new_rope
569 end
570
571 # Unsafe method to convert self as an ImmutableRope
572 #
573 # To be used internally only
574 private fun to_immutable: ImmutableRope
575 do
576 var immutable_self = new ImmutableRope
577 immutable_self.parent_node = self.parent_node
578 return immutable_self
579 end
580
581 ############################################################################
582 # Rope refined Methods #
583 ############################################################################
584
585 redef fun subrope(index_from: Int, count: Int): BufferRope
586 do
587 return super.as(BufferRope)
588 end
589
590 redef fun *(repeats: Int): BufferRope
591 do
592 return super.as(BufferRope)
593 end
594
595 redef fun +(other: Rope): BufferRope
596 do
597 return super.as(BufferRope)
598 end
599
600 redef fun multi_concat(ropes: Rope...): BufferRope
601 do
602 return super.as(BufferRope)
603 end
604
605 # Refines to add a cache method, calculates only once for every modification
606 # the string representation for self
607 redef fun to_s
608 do
609 if self.str_representation == null or is_dirty then
610 self.str_representation = flatten
611 is_dirty = false
612 end
613 return self.str_representation.as(not null)
614 end
615
616 end
617
618 # Rope that cannot be modified
619 class ImmutableRope
620 super Rope
621
622 init
623 do
624 super
625 end
626
627 redef init with_string(str)
628 do
629 super
630 end
631
632 ############################################################################
633 # Rope refined Methods #
634 ############################################################################
635
636 redef fun subrope(index_from: Int, count: Int): ImmutableRope
637 do
638 return (super.as(BufferRope)).to_immutable
639 end
640
641 redef fun *(repeats: Int): ImmutableRope
642 do
643 return (super.as(BufferRope)).to_immutable
644 end
645
646 redef fun +(other: Rope): ImmutableRope
647 do
648 return (super.as(BufferRope)).to_immutable
649 end
650
651 redef fun multi_concat(ropes: Rope...): ImmutableRope
652 do
653 return (super.as(BufferRope)).to_immutable
654 end
655
656 end
657
658 ############################################
659 # Rope view classes #
660 ############################################
661
662 class CharRopeView
663 super SequenceRead[Char]
664
665 # Targeted Rope for the view
666 private var target: Rope
667
668 init(tgt: Rope)
669 do
670 self.target = tgt
671 end
672
673 redef fun [](position)
674 do
675 var tuple = target.get_node_for_pos(position)
676 return tuple.curr_node.value[tuple.corrected_pos]
677 end
678
679 redef fun first do return self[0]
680
681 redef fun index_of(char)
682 do
683 var intern_iter = new RopeCharIterator(target)
684 while intern_iter.is_ok do
685 if intern_iter.item == char then return intern_iter.index
686 intern_iter.next
687 end
688 return -1
689 end
690
691 redef fun iterator do
692 return new RopeCharIterator(target)
693 end
694
695 redef fun last do return self[self.length-1]
696
697 redef fun length do return target.length
698
699 redef fun count(item)
700 do
701 var count = 0
702 var iter = self.iterator
703
704 for i in self do
705 if i == item then count += 1
706 end
707
708 return count
709 end
710
711 redef fun has_only(item)
712 do
713 for i in self do
714 if i != item then return false
715 end
716 return true
717 end
718
719 redef fun is_empty do return length == 0
720
721 redef fun to_a do
722 return to_s.to_a
723 end
724
725 redef fun to_s do
726 return target.to_s
727 end
728
729 redef fun ==(other)
730 do
731 if not other isa SequenceRead[Char] then return false
732
733 if self.length != other then return false
734
735 var iter = other.iterator
736
737 for i in self do
738 if i != iter.item then return false
739 iter.next
740 end
741
742 return true
743 end
744
745 end
746
747 ###########################################
748 # Iterator classes #
749 ###########################################
750
751 # A tuple representing the state of a node for a tree parsing algorithm
752 private class TupleVisitNode
753
754 init(tgt: ConcatNode)
755 do
756 self.node = tgt
757 end
758
759 private var node: ConcatNode
760
761 private var left_visited = false
762 private var right_visited = false
763
764 end
765
766 # Any kind of iterator parsing a Rope for LeafNodes
767 private abstract class RopeIterator
768 super IndexedIterator[LeafNode]
769
770 # Rope meant to be visited
771 private var _target: Rope
772
773 private fun target: Rope do return self._target
774
775 # Position in target
776 private var pos = 0
777
778 init(tgt: Rope)
779 do
780 self._target = tgt
781 end
782
783 init with_index(tgt: Rope, index: Int)
784 do
785 self._target = tgt
786 end
787
788 end
789
790 # Iterator returning the content of a rope one char at a time
791 class RopeCharIterator
792 super IndexedIterator[Char]
793
794 # The iterator used to visit the rope
795 private var sub_str_iter: DFSRopeLeafIterator
796
797 # The current position in the rope
798 private var abs_pos = 0
799
800 # The position in the current substring
801 private var sub_pos: Int = 0
802
803 # The substring contained in the current node visited by `sub_str_iter`
804 private var curr_substring: nullable String
805
806 init(tgt: Rope)
807 do
808 sub_str_iter = new DFSRopeLeafIterator(tgt)
809 if sub_str_iter.is_ok then curr_substring = sub_str_iter.item.value
810 end
811
812 redef fun item do return curr_substring[sub_pos]
813
814 redef fun is_ok
815 do
816 if sub_str_iter.is_ok then return true
817 if not sub_str_iter.is_ok and curr_substring != null and sub_pos < curr_substring.length then return true
818 return false
819 end
820
821 redef fun next
822 do
823 assert is_ok
824 if sub_pos < curr_substring.length - 1 then
825 sub_pos += 1
826 else
827 sub_str_iter.next
828 if sub_str_iter.is_ok then
829 curr_substring = sub_str_iter.item.value
830 sub_pos = 0
831 else
832 sub_pos = curr_substring.length
833 end
834 end
835 abs_pos += 1
836 end
837
838 redef fun index do return abs_pos
839
840 end
841
842 # Special kind of iterator
843 #
844 # Performs a Depth-First Search on RopeLeaf items
845 #
846 private class DFSRopeLeafIterator
847 super RopeIterator
848
849 # Stack of the visited nodes in the rope
850 private var visit_stack = new List[TupleVisitNode]
851
852 # The leaf node being visited by the iterator
853 private var curr_leaf: nullable LeafNode
854
855 init(tgt: Rope)
856 do
857 super
858
859 var first_node = target.parent_node
860
861 if first_node isa ConcatNode then
862 visit_stack.push(new TupleVisitNode(first_node))
863 else if first_node isa LeafNode then
864 curr_leaf = first_node
865 return
866 end
867
868 next_body
869 end
870
871 # Creates a new iterator on `tgt` starting at `index`
872 redef init with_index(tgt: Rope, index: Int)
873 do
874 super
875
876 var returned_tuple = target.get_node_for_pos(index)
877 curr_leaf = returned_tuple.curr_node
878 visit_stack = returned_tuple.visit_stack
879 pos = index - returned_tuple.corrected_pos
880 end
881
882 redef fun is_ok do return curr_leaf != null
883
884 redef fun next
885 do
886 assert is_ok
887 pos += curr_leaf.value.length
888 next_body
889 end
890
891 private fun next_body
892 do
893 var next_node: nullable RopeNode
894 while not visit_stack.is_empty do
895 var curr_concat_tuple = visit_stack.last
896 if not curr_concat_tuple.left_visited then
897
898 curr_concat_tuple.left_visited = true
899
900 next_node = curr_concat_tuple.node.left_child
901
902 if next_node == null then continue
903
904 if next_node isa ConcatNode then
905 visit_stack.push(new TupleVisitNode(next_node))
906 else if next_node isa LeafNode then
907 curr_leaf = next_node
908 return
909 end
910
911 else if not curr_concat_tuple.right_visited then
912
913 curr_concat_tuple.right_visited = true
914
915 next_node = curr_concat_tuple.node.right_child
916
917 if next_node == null then continue
918
919 if next_node isa ConcatNode then
920 visit_stack.push(new TupleVisitNode(next_node))
921 else if next_node isa LeafNode then
922 curr_leaf = next_node
923 return
924 end
925
926 else
927 visit_stack.pop
928 end
929 end
930 self.curr_leaf = null
931 end
932
933 redef fun item
934 do
935 assert is_ok
936 return curr_leaf.as(not null)
937 end
938
939 end
940
941 ###########################################
942 # Node classes #
943 ###########################################
944
945 # A node for a Rope
946 private abstract class RopeNode
947
948 private var _length = 0
949
950 private var parent: nullable ConcatNode = null
951
952 private var height = 0
953
954 # The balance factor of a node, if it is a Leaf, it equals its length
955 # Else, it will be equal to the difference between the height on the left and on the right
956 private fun balance_factor: Int do return height end
957
958 fun length: Int do return _length
959
960 private fun length=(len: Int)
961 do
962 _length = len
963 end
964 end
965
966 # Node that represents a concatenation between two nodes (of any RopeNode type)
967 private class ConcatNode
968 super RopeNode
969
970 private var _left_child: nullable RopeNode = null
971 private var _right_child: nullable RopeNode = null
972
973 private fun left_child: nullable RopeNode
974 do
975 if _left_child != null then
976 return _left_child
977 else
978 return null
979 end
980 end
981
982 private fun right_child: nullable RopeNode
983 do
984 if _right_child != null then
985 return _right_child
986 else
987 return null
988 end
989 end
990
991 private fun left_child=(new_node: nullable RopeNode)
992 do
993 self._left_child = new_node
994 new_node.parent = self
995 update_data
996 end
997
998 private fun right_child=(new_node: nullable RopeNode)
999 do
1000 self._right_child = new_node
1001 new_node.parent = self
1002 update_data
1003 end
1004
1005 # Updates the internal data of the current node
1006 #
1007 # Concretely, updates the length and the height of the node
1008 private fun update_data
1009 do
1010 self.length = 0
1011 self.height = 1
1012 if left_child != null then
1013 self.length += left_child.length
1014 if left_child.height + 1 > self.height then self.height = left_child.height + 1
1015 end
1016 if right_child != null then
1017 self.length += right_child.length
1018 if right_child.height + 1 > self.height then self.height = right_child.height + 1
1019 end
1020 end
1021
1022 # Computes and returns the balance factor (used for AVL trees)
1023 #
1024 # Formula : left.height - right.height
1025 redef private fun balance_factor
1026 do
1027 var left_height = 0
1028 var right_height = 0
1029 if left_child != null then left_height = left_child.height
1030 if right_child != null then right_height = right_child.height
1031 return left_height - right_height
1032 end
1033 end
1034
1035 # A leaf node contains a substring of some sort
1036 private class LeafNode
1037 super RopeNode
1038
1039 # Encapsulated string in the leaf node
1040 private var _value: String
1041
1042 init(val: String)
1043 do
1044 self._value = val.to_s
1045 self.length = val.length
1046 end
1047
1048 private fun value: String do return self._value
1049
1050 private fun value= (val: String)
1051 do
1052 _value = val
1053 end
1054 end
1055
1056 #####################################################
1057 # Foreign classes refinement #
1058 #####################################################
1059
1060 redef class String
1061 redef fun ==(other)
1062 do
1063 if other isa Rope then
1064 return other == self
1065 else
1066 return super
1067 end
1068 end
1069 end
1070
1071 redef class Buffer
1072 redef fun ==(other)
1073 do
1074 if other isa Rope then
1075 return other == self
1076 else
1077 return super
1078 end
1079 end
1080 end