3 using System.Collections;
4 using System.Collections.Generic;
10 public enum MTextFormat
12 MTEXT_FORMAT_US_ASCII,
14 MTEXT_FORMAT_UTF_16BE,
15 MTEXT_FORMAT_UTF_16LE,
16 MTEXT_FORMAT_UTF_32BE,
17 MTEXT_FORMAT_UTF_32LE,
21 public class MTextProperty
25 private bool front_sticky;
26 private bool rear_sticky;
28 public MSymbol Key { get { return key; } }
29 public object Val { get { return val; } }
30 public bool FrontSticky { get { return front_sticky; } }
31 public bool RearSticky { get { return rear_sticky; } }
33 public MTextProperty (MSymbol key, object val)
39 public MTextProperty (MSymbol key, object val,
40 bool front_sticky, bool rear_sticky)
44 this.front_sticky = front_sticky;
45 this.rear_sticky = rear_sticky;
49 public class MText : IEnumerable, IEquatable<MText>, IComparable<MText>
52 public enum MTextFormat format;
54 private StringBuilder sb;
56 private int cache_pos;
57 private int cache_idx;
58 private MPlist intervals;
59 private MPlist default_property;
60 private bool read_only;
62 private static UTF8Encoding utf8 = new UTF8Encoding ();
64 private static int count_chars (String str)
66 int len = str.Length, n = 0;
68 for (int i = 0; i < len; i++, n++)
69 if (surrogate_high_p (str[i]))
74 private static int count_chars (StringBuilder str)
76 int len = str.Length, n = 0;
78 for (int i = 0; i < len; i++, n++)
79 if (surrogate_high_p (str[i]))
86 sb = new StringBuilder ();
87 intervals = new MPlist ();
90 public MText (byte[] str)
92 sb = new StringBuilder (utf8.GetString (str));
93 nchars = count_chars (sb);
94 intervals = new MPlist ();
97 public MText (String str)
99 sb = new StringBuilder (str);
100 nchars = count_chars (str);
101 intervals = new MPlist ();
104 public MText (StringBuilder str)
107 nchars = count_chars (str);
108 intervals = new MPlist ();
111 public static MText operator+ (MText mt1, MText mt2)
113 MText mt = new MText ();
115 mt.sb.Append (mt1.sb);
116 mt.sb.Append (mt2.sb);
117 mt.nchars = mt1.nchars + mt2.nchars;
122 public bool ReadOnly { get { return read_only; } }
123 public int Length { get { return nchars; } }
127 // for IEnumerable interface
128 public IEnumerator GetEnumerator() { return new MTextEnum (this); }
130 // for IEquatable interface
131 public bool Equals (MText other) { return this.sb.Equals (other.sb); }
133 // for IComparable interface
134 public int CompareTo (MText other)
136 return this.sb.ToString ().CompareTo (other.sb.ToString ());
139 public override String ToString () { return sb.ToString (); }
141 private static bool surrogate_high_p (char c)
143 return (c >= 0xD800 && c < 0xDC00);
146 private static bool surrogate_low_p (char c)
148 return (c >= 0xDC00 && c < 0xE000);
151 private static int inc_idx (StringBuilder sb, int i)
153 return (i + (surrogate_high_p (sb[i]) ? 2 : 1));
156 private static int dec_idx (StringBuilder sb, int i)
158 return (i - (surrogate_low_p (sb[i - 1]) ? 2 : 1));
161 private static int pos_to_idx (MText mt, int pos)
163 if (pos == mt.cache_pos)
169 if (pos < mt.cache_pos)
171 if (mt.cache_pos == mt.cache_idx)
173 if (pos < mt.cache_pos - pos)
180 p = mt.cache_pos; i = mt.cache_idx;
186 if (mt.nchars - mt.cache_pos == mt.sb.Length - mt.cache_idx)
187 return (mt.cache_idx + pos - mt.cache_pos);
188 if (pos - mt.cache_pos < mt.nchars - pos)
190 p = mt.cache_pos; i = mt.cache_idx;
195 p = mt.nchars; i = mt.sb.Length;
200 for (; p < pos; i = inc_idx (mt.sb, i), p++);
202 for (; p > pos; i = dec_idx (mt.sb, i), p--);
208 private void insert (int pos, MText mt2, int from, int to)
210 int pos_idx = pos_to_idx (this, pos);
211 int from_idx = pos_to_idx (mt2, from);
212 int to_idx = pos_to_idx (mt2, to);
214 sb.Insert (pos_idx, mt2.sb.ToString (from_idx, to_idx - from_idx));
217 foreach (MPlist plist in mt2.intervals)
218 if (intervals.find (plist.Key) == null)
219 intervals.push (plist.Key, new MInterval (plist.Key, this));
220 foreach (MPlist plist in intervals)
222 MPlist p = mt2.intervals.find (plist.Key);
226 interval = new MInterval (plist.Key, to - from);
228 interval = ((MInterval) p.Val).copy (from, to);
229 ((MInterval) plist.Val).insert (pos, interval);
233 public int this[int i]
236 i = pos_to_idx (this, i);
239 if (surrogate_high_p (sb[i]))
241 sb[i] = (char) value;
245 char high = (char) (0xD800 + ((value - 0x10000) >> 10));
246 char low = (char) (0xDC00 + ((value - 0x10000) & 0x3FF));
248 if (! surrogate_high_p (sb[i]))
255 i = pos_to_idx (this, i);
256 return (surrogate_high_p (sb[i])
257 ? ((sb[i] - 0xD800) << 10) + (sb[i + 1] - 0xDC00) + 0x10000
264 return (new MText (sb.ToString ()));
267 public MText ins (int pos, MText mt)
269 insert (pos, mt, 0, mt.nchars);
273 public MText ins (int pos, MText mt, int from, int to)
275 insert (pos, mt, from, to);
279 public MText del (int from, int to)
281 sb.Remove (from, pos_to_idx (this, to) - pos_to_idx (this, from));
284 foreach (MPlist plist in intervals)
285 ((MInterval) plist.Val).delete (from, to);
289 public object get_prop (int pos, MSymbol key)
291 MInterval i = (MInterval) intervals.find (key).Val;
296 MTextProperty prop = i.get (pos);
297 return (prop != null ? prop.Val : null);
300 public object get_prop (int pos, MSymbol key, out MTextProperty prop)
302 MInterval i = (MInterval) intervals.find (key).Val;
305 return (prop = null);
307 return (prop != null ? prop.Val : null);
310 public object get_prop (int pos, MSymbol key, out MTextProperty[] array)
312 MInterval i = (MInterval) intervals.find (key).Val;
315 return (array = null);
316 MTextProperty prop = i.get (pos, out array);
317 return (prop != null ? prop.Val : null);
320 public void push_prop (int from, int to, MSymbol key, object val)
322 push_prop (from, to, new MTextProperty (key, val));
325 public void push_prop (int from, int to, MTextProperty prop)
329 if (default_property == null)
330 default_property = new MPlist ();
331 default_property.push (prop.key, prop.val);
335 MInterval root = (MInterval) intervals.find (prop.key).Val;
339 root = new MInterval (prop.key, this);
340 intervals.push (prop.key, root);
342 root.Push (from, to, prop);
346 private class MInterval
348 // position: 0 1 2 3 4 5 6 7
349 // | A | B | C | D | E F | G |
350 // interval |---|---|---|<->|-------|---|
351 // |---|<->|---| |<----->|---|
355 // [3 (1 2)] [3 (4 6)]
356 // [1 (0 1)] [2 (2 3)] [1 (6 7)]
357 private int total_length;
358 private int from, to;
360 private Stack<MTextProperty> stack;
361 private MInterval left, right, parent;
364 public MInterval (MSymbol key, int length)
367 throw new Exception ("Invalid interval length");
369 total_length = length;
370 stack = new Stack<MTextProperty> ();
373 public MInterval (MSymbol key, MText mt)
377 total_length = mt.sb.Length;
380 stack = new Stack<MTextProperty> ();
383 public MTextProperty get (int pos)
385 MInterval i = find (pos);
387 return (i.stack.Count > 0 ? i.stack.Peek () : null);
390 public MTextProperty get (int pos, out MTextProperty[] array)
392 MInterval i = find (pos);
394 if (i.stack.Count == 0)
399 array = i.stack.ToArray ();
400 return i.stack.Peek ();
403 private MInterval (MSymbol key, int length, Stack<MTextProperty> stack)
406 total_length = length;
409 stack = new Stack<MTextProperty> (stack);
413 private void update_from_to ()
417 from = parent.from - total_length + LeftLength;
418 to = parent.from - RightLength;
422 private int LeftLength
424 get { return (left == null ? 0 : left.total_length); }
427 private int RightLength
429 get { return (right == null ? 0 : right.total_length); }
432 private MInterval LeftMostNode
434 get { return (left == null ? this : left.LeftMostNode); }
437 private MInterval RightMostNode
439 get { return (right == null ? this : right.RightMostNode); }
442 private MInterval LeftNode {
447 for (i = left; i.right != null; i = i.right);
449 for (i = parent; i != null && i.left == null; i = i.parent);
454 private MInterval RightNode {
459 for (i = right; i.left != null; i = i.left);
461 for (i = parent; i != null && i.right == null; i = i.parent);
466 private MInterval find (int pos)
470 return left.find (pos);
472 return right.find (pos);
476 // p-. or .-p p-. or .-p
477 // .-this-. .-right-.
478 // left .-right-. -> .-this-. c2
480 private MInterval promote_right ()
482 int right_length = right.total_length;
486 mtext.intervals.put (key, right);
487 else if (parent.left == this)
490 parent.right = right;
491 right.parent = parent;
497 parent.total_length += total_length;
498 total_length -= right_length;
502 parent.total_length -= c1.total_length;
503 total_length += c1.total_length;
508 // p-. or .-p p-. or .-p
510 // .-left-. .-right-. -> c1 .-this-.
512 private MInterval promote_left ()
514 int left_length = left.total_length;
518 mtext.intervals.put (key, left);
519 else if (parent.left == this)
523 left.parent = parent;
529 parent.total_length += total_length;
530 total_length -= left_length;
534 parent.total_length -= c1.total_length;
535 total_length += c1.total_length;
540 private MInterval balance ()
547 // .-left-. .-right-.
549 int diff = i.LeftLength - i.RightLength;
554 new_diff = (i.total_length - i.LeftLength
555 + i.left.RightLength - i.left.LeftLength);
556 if (Math.Abs (new_diff) >= diff)
558 i = i.promote_left ();
563 new_diff = (i.total_length - i.RightLength
564 + i.right.LeftLength - i.right.RightLength);
565 if (Math.Abs (new_diff) >= diff)
567 i = i.promote_right ();
574 public MInterval copy (int start, int end)
576 MInterval this_copy, left_copy = null, right_copy = null;
582 return left.copy (start, end);
583 left_copy = left.copy (start, from);
588 return right.copy (start, end);
589 right_copy = right.copy (to, end);
591 this_copy = new MInterval (key, end - start, stack);
592 this_copy.left = left_copy;
593 this_copy.right = right_copy;
601 private MInterval divide_right (int pos)
605 MInterval interval = new MInterval (key, to - pos, stack);
607 total_length -= to - pos;
610 right.parent = interval;
611 interval.total_length += right.total_length;
613 interval.parent = this;
621 private MInterval divide_left (int pos)
625 MInterval interval = new MInterval (key, to - pos, stack);
627 total_length -= to - pos;
630 left.parent = interval;
631 interval.total_length += left.total_length;
633 interval.parent = this;
638 public void insert (int pos, MInterval interval)
643 LeftNode.insert (pos, interval);
648 RightNode.insert (pos, interval);
653 divide_right (pos).insert (pos, interval);
658 if (left != null && LeftNode.stack.Count > 0)
660 Stack<MTextProperty> s = new Stack<MTextProperty> ();
662 foreach (MTextProperty p in LeftNode.stack)
667 for (MInterval i = interval.LeftMostNode;
668 i != null && i.stack.Count == 0;
670 foreach (MTextProperty p in s)
676 Stack<MTextProperty> s = new Stack<MTextProperty> ();
678 foreach (MTextProperty p in stack)
683 for (MInterval i = interval.RightMostNode;
684 i != null && i.stack.Count == 0;
686 foreach (MTextProperty p in s)
691 // INTERVAL is ready to insert.
693 // .-this-. ==> .-this-.
700 MInterval i = left.RightMostNode;
704 for (; i != null; i = i.parent)
705 i.total_length += interval.total_length;
711 for (MInterval i = this; i != null; i = i.parent)
712 i.total_length += interval.total_length;
716 private void update_parent (MInterval i)
719 mtext.intervals.put (key, i);
724 if (parent.right == i)
726 diff = parent.right.total_length - i.total_length;
731 diff = parent.left.total_length - i.total_length;
734 for (i = parent; i != null; i = i.parent)
735 i.total_length += diff;
739 public void delete (int start, int end)
746 left.delete (start, end);
749 left.delete (start, from);
756 right.delete (start, end);
759 right.delete (to, end);
762 if (start == from && end == to)
765 update_parent (right);
766 else if (right == null)
767 update_parent (left);
772 for (i = right; i.left != null; i = i.left)
773 i.total_length += left.total_length;
774 i.total_length += left.total_length;
776 update_parent (right);
781 int len = end - start;
783 for (MInterval i = this; i != null; i = i.parent)
784 i.total_length -= len;
788 public MTextProperty Push (int start, int end, MTextProperty prop)
793 left.Push (start, end, prop);
800 right.Push (start, end, prop);
815 private class MTextEnum : IEnumerator
818 private int pos = -1;
820 public MTextEnum (MText mt)
825 public bool MoveNext ()
828 return (pos < mt.nchars);
836 public object Current
839 //if (pos < 0 || pos >= mt.nchars)
840 //throw new InvalidOperationException ();