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
27 internal enum Flag : byte
36 public MSymbol Key { get { return key; } }
37 public object Val { get { return val; } }
38 public bool FrontSticky
40 get { return (flags & Flag.FrontSticky) != Flag.None; }
42 public bool RearSticky
44 get { return (flags & Flag.RearSticky) != Flag.None; }
48 get { return (flags & Flag.Sensitive) != Flag.None; }
51 public MTextProperty (MSymbol key, object val)
55 flags |= Flag.RearSticky;
58 public MTextProperty (MSymbol key, object val,
59 bool front_sticky, bool rear_sticky, bool sensitive)
64 flags |= Flag.FrontSticky;
66 flags |= Flag.RearSticky;
68 flags |= Flag.Sensitive;
72 public class MText : IEnumerable, IEquatable<MText>, IComparable<MText>
75 public enum MTextFormat format;
77 private StringBuilder sb;
79 private int cache_pos;
80 private int cache_idx;
81 private MPlist intervals;
82 private MPlist default_property;
83 private bool read_only;
85 private static UTF8Encoding utf8 = new UTF8Encoding ();
87 private static int count_chars (String str)
89 int len = str.Length, n = 0;
91 for (int i = 0; i < len; i++, n++)
92 if (surrogate_high_p (str[i]))
97 private static int count_chars (StringBuilder str)
99 int len = str.Length, n = 0;
101 for (int i = 0; i < len; i++, n++)
102 if (surrogate_high_p (str[i]))
109 sb = new StringBuilder ();
110 intervals = new MPlist ();
113 public MText (byte[] str)
115 sb = new StringBuilder (utf8.GetString (str));
116 nchars = count_chars (sb);
117 intervals = new MPlist ();
120 public MText (String str)
122 sb = new StringBuilder (str);
123 nchars = count_chars (str);
124 intervals = new MPlist ();
127 public MText (StringBuilder str)
130 nchars = count_chars (str);
131 intervals = new MPlist ();
134 public static MText operator+ (MText mt1, MText mt2)
136 MText mt = new MText ();
138 mt.sb.Append (mt1.sb);
139 mt.sb.Append (mt2.sb);
140 mt.nchars = mt1.nchars + mt2.nchars;
145 public bool ReadOnly { get { return read_only; } }
146 public int Length { get { return nchars; } }
150 // for IEnumerable interface
151 public IEnumerator GetEnumerator() { return new MTextEnum (this); }
153 // for IEquatable interface
154 public bool Equals (MText other) { return this.sb.Equals (other.sb); }
156 // for IComparable interface
157 public int CompareTo (MText other)
159 return this.sb.ToString ().CompareTo (other.sb.ToString ());
162 public override String ToString () { return "\"" + sb.ToString () + "\""; }
164 private static bool surrogate_high_p (char c)
166 return (c >= 0xD800 && c < 0xDC00);
169 private static bool surrogate_low_p (char c)
171 return (c >= 0xDC00 && c < 0xE000);
174 private static int inc_idx (StringBuilder sb, int i)
176 return (i + (surrogate_high_p (sb[i]) ? 2 : 1));
179 private static int dec_idx (StringBuilder sb, int i)
181 return (i - (surrogate_low_p (sb[i - 1]) ? 2 : 1));
184 private static int pos_to_idx (MText mt, int pos)
186 if (pos == mt.cache_pos)
192 if (pos < mt.cache_pos)
194 if (mt.cache_pos == mt.cache_idx)
196 if (pos < mt.cache_pos - pos)
203 p = mt.cache_pos; i = mt.cache_idx;
209 if (mt.nchars - mt.cache_pos == mt.sb.Length - mt.cache_idx)
210 return (mt.cache_idx + pos - mt.cache_pos);
211 if (pos - mt.cache_pos < mt.nchars - pos)
213 p = mt.cache_pos; i = mt.cache_idx;
218 p = mt.nchars; i = mt.sb.Length;
223 for (; p < pos; i = inc_idx (mt.sb, i), p++);
225 for (; p > pos; i = dec_idx (mt.sb, i), p--);
231 private void insert (int pos, MText mt2, int from, int to)
233 int pos_idx = pos_to_idx (this, pos);
234 int from_idx = pos_to_idx (mt2, from);
235 int to_idx = pos_to_idx (mt2, to);
237 sb.Insert (pos_idx, mt2.sb.ToString (from_idx, to_idx - from_idx));
240 foreach (MPlist plist in mt2.intervals)
241 if (intervals.find (plist.Key) == null)
242 intervals.push (plist.Key, new MInterval (plist.Key, this));
243 foreach (MPlist plist in intervals)
245 MPlist p = mt2.intervals.find (plist.Key);
249 interval = new MInterval (plist.Key, to - from);
251 interval = ((MInterval) p.Val).copy (from, to);
252 ((MInterval) plist.Val).insert (pos, interval);
256 public int this[int i]
259 i = pos_to_idx (this, i);
262 if (surrogate_high_p (sb[i]))
264 sb[i] = (char) value;
268 char high = (char) (0xD800 + ((value - 0x10000) >> 10));
269 char low = (char) (0xDC00 + ((value - 0x10000) & 0x3FF));
271 if (! surrogate_high_p (sb[i]))
278 i = pos_to_idx (this, i);
279 return (surrogate_high_p (sb[i])
280 ? ((sb[i] - 0xD800) << 10) + (sb[i + 1] - 0xDC00) + 0x10000
287 return (new MText (sb.ToString ()));
290 public MText ins (int pos, MText mt)
292 insert (pos, mt, 0, mt.nchars);
296 public MText ins (int pos, MText mt, int from, int to)
298 insert (pos, mt, from, to);
302 public MText del (int from, int to)
304 sb.Remove (from, pos_to_idx (this, to) - pos_to_idx (this, from));
307 foreach (MPlist plist in intervals)
308 ((MInterval) plist.Val).delete (from, to);
312 public object get_prop (int pos, MSymbol key)
314 MInterval i = (MInterval) intervals.find (key).Val;
319 MTextProperty prop = i.get (pos);
320 return (prop != null ? prop.Val : null);
323 public object get_prop (int pos, MSymbol key, out MTextProperty prop)
325 MInterval i = (MInterval) intervals.find (key).Val;
328 return (prop = null);
330 return (prop != null ? prop.Val : null);
333 public object get_prop (int pos, MSymbol key, out MTextProperty[] array)
335 MInterval i = (MInterval) intervals.find (key).Val;
338 return (array = null);
339 MTextProperty prop = i.get (pos, out array);
340 return (prop != null ? prop.Val : null);
343 public void push_prop (int from, int to, MSymbol key, object val)
345 push_prop (from, to, new MTextProperty (key, val));
348 public void push_prop (int from, int to, MTextProperty prop)
352 if (default_property == null)
353 default_property = new MPlist ();
354 default_property.push (prop.key, prop.val);
358 MInterval root = (MInterval) intervals.find (prop.key).Val;
362 root = new MInterval (prop.key, this);
363 intervals.push (prop.key, root);
365 root.Push (from, to, prop);
369 private class MInterval
371 // position: 0 1 2 3 4 5 6 7
372 // | A | B | C | D | E F | G |
373 // interval |---|---|---|<->|-------|---|
374 // |---|<->|---| |<----->|---|
378 // [3 (1 2)] [3 (4 6)]
379 // [1 (0 1)] [2 (2 3)] [1 (6 7)]
380 private int total_length;
381 private int from, to;
383 private MPlist stack;
384 private MInterval left, right, parent;
387 public MInterval (MSymbol key, int length)
390 throw new Exception ("Invalid interval length");
392 total_length = length;
393 stack = new MPlist ();
396 public MInterval (MSymbol key, MText mt)
400 total_length = mt.sb.Length;
403 stack = new MPlist ();
406 public MTextProperty get (int pos)
408 MInterval i = find (pos);
410 return (i.stack.IsEmpty ? null : (MTextProperty) i.stack.Val);
413 public MTextProperty get (int pos, out MTextProperty[] array)
415 MInterval i = find (pos);
422 array = new MTextProperty[i.stack.Count];
426 for (idx = 0, p = i.stack; ! p.IsEmpty; idx++, p = p.Next)
427 array[idx] = (MTextProperty) p.Val;
428 return array[idx - 1];
431 private MInterval (MSymbol key, int length, MPlist stack)
434 total_length = length;
437 this.stack = stack.Clone ();
440 private void update_from_to ()
444 from = parent.from - total_length + LeftLength;
445 to = parent.from - RightLength;
449 private int LeftLength
451 get { return (left == null ? 0 : left.total_length); }
454 private int RightLength
456 get { return (right == null ? 0 : right.total_length); }
459 private MInterval left_most_node
461 get { return (left == null ? this : left.left_most_node); }
464 private MInterval right_most_node
466 get { return (right == null ? this : right.right_most_node); }
469 private MInterval prev {
474 for (i = left; i.right != null; i = i.right);
476 for (i = parent; i != null && i.left == null; i = i.parent);
481 private MInterval next {
486 for (i = right; i.left != null; i = i.left);
488 for (i = parent; i != null && i.right == null; i = i.parent);
493 private MInterval find (int pos)
497 return left.find (pos);
499 return right.find (pos);
503 // p-. or .-p p-. or .-p
504 // .-this-. .-right-.
505 // left .-right-. -> .-this-. c2
507 private MInterval promote_right ()
509 int right_length = right.total_length;
513 mtext.intervals.put (key, right);
514 else if (parent.left == this)
517 parent.right = right;
518 right.parent = parent;
524 parent.total_length += total_length;
525 total_length -= right_length;
529 parent.total_length -= c1.total_length;
530 total_length += c1.total_length;
535 // p-. or .-p p-. or .-p
537 // .-left-. .-right-. -> c1 .-this-.
539 private MInterval promote_left ()
541 int left_length = left.total_length;
545 mtext.intervals.put (key, left);
546 else if (parent.left == this)
550 left.parent = parent;
556 parent.total_length += total_length;
557 total_length -= left_length;
561 parent.total_length -= c1.total_length;
562 total_length += c1.total_length;
567 private MInterval balance ()
574 // .-left-. .-right-.
576 int diff = i.LeftLength - i.RightLength;
581 new_diff = (i.total_length - i.LeftLength
582 + i.left.RightLength - i.left.LeftLength);
583 if (Math.Abs (new_diff) >= diff)
585 i = i.promote_left ();
590 new_diff = (i.total_length - i.RightLength
591 + i.right.LeftLength - i.right.RightLength);
592 if (Math.Abs (new_diff) >= diff)
594 i = i.promote_right ();
601 public MInterval copy (int start, int end)
603 MInterval this_copy, left_copy = null, right_copy = null;
609 return left.copy (start, end);
610 left_copy = left.copy (start, from);
615 return right.copy (start, end);
616 right_copy = right.copy (to, end);
618 this_copy = new MInterval (key, end - start, stack);
619 this_copy.left = left_copy;
620 this_copy.right = right_copy;
628 private MInterval divide_right (int pos)
632 MInterval interval = new MInterval (key, to - pos, stack);
634 total_length -= to - pos;
637 right.parent = interval;
638 interval.total_length += right.total_length;
640 interval.parent = this;
648 private MInterval divide_left (int pos)
652 MInterval interval = new MInterval (key, to - pos, stack);
654 total_length -= to - pos;
657 left.parent = interval;
658 interval.total_length += left.total_length;
660 interval.parent = this;
665 private void remove_properties (MTextProperty.Flag flags)
667 for (MPlist p = stack; ! p.IsEmpty;)
669 MTextProperty prop = (MTextProperty) p.Val;
671 if ((prop.flags & flags) == flags)
678 private void merge_properties (MPlist plist, MTextProperty.Flag flags)
681 left.merge_properties (plist, flags);
683 right.merge_properties (plist, flags);
684 for (MPlist p = plist; ! p.IsEmpty; p = p.Next)
686 MTextProperty prop = (MTextProperty) p.Val;
688 if ((prop.flags & flags) == flags
689 && stack.get (prop.Key) == null)
690 stack.push (prop.key, prop);
694 public void insert (int pos, MInterval interval)
697 if (pos < from || (pos == from && left == null && pos > 0))
699 prev.insert (pos, interval);
702 if (pos > to || (pos == to && right == null && next != null))
704 next.insert (pos, interval);
707 if (pos > from && pos < to)
709 remove_properties (MTextProperty.Flag.Sensitive);
710 divide_right (pos).insert (pos, interval);
717 prev.remove_properties
718 (MTextProperty.Flag.Sensitive|MTextProperty.Flag.RearSticky);
719 interval.merge_properties
720 (prev.stack, MTextProperty.Flag.RearSticky);
723 (MTextProperty.Flag.Sensitive|MTextProperty.Flag.FrontSticky);
724 interval.merge_properties
725 (stack, MTextProperty.Flag.FrontSticky);
727 // INTERVAL is ready to insert.
729 // .-this-. ==> .-this-.
736 MInterval i = left.right_most_node;
740 for (; i != null; i = i.parent)
741 i.total_length += interval.total_length;
747 for (MInterval i = this; i != null; i = i.parent)
748 i.total_length += interval.total_length;
755 MInterval left_most = right.left_most_node;
757 left_most.remove_properties
758 (MTextProperty.Flag.Sensitive|MTextProperty.Flag.FrontSticky);
759 interval.merge_properties
760 (stack, MTextProperty.Flag.FrontSticky);
763 (MTextProperty.Flag.Sensitive|MTextProperty.Flag.RearSticky);
764 interval.merge_properties
765 (stack, MTextProperty.Flag.RearSticky);
767 // INTERVAL is ready to insert.
769 // .-this-. ==> .-this-.
776 MInterval i = right.left_most_node;
780 for (; i != null; i = i.parent)
781 i.total_length += interval.total_length;
787 for (MInterval i = this; i != null; i = i.parent)
788 i.total_length += interval.total_length;
793 private void update_parent (MInterval i)
796 mtext.intervals.put (key, i);
801 if (parent.right == i)
803 diff = parent.right.total_length - i.total_length;
808 diff = parent.left.total_length - i.total_length;
811 for (i = parent; i != null; i = i.parent)
812 i.total_length += diff;
816 public void delete (int start, int end)
823 left.delete (start, end);
826 left.delete (start, from);
833 right.delete (start, end);
836 right.delete (to, end);
839 if (start == from && end == to)
842 update_parent (right);
843 else if (right == null)
844 update_parent (left);
849 for (i = right; i.left != null; i = i.left)
850 i.total_length += left.total_length;
851 i.total_length += left.total_length;
853 update_parent (right);
858 int len = end - start;
860 for (MInterval i = this; i != null; i = i.parent)
861 i.total_length -= len;
865 public MTextProperty Push (int start, int end, MTextProperty prop)
870 left.Push (start, end, prop);
877 right.Push (start, end, prop);
887 stack.push (prop.key, prop);
892 private class MTextEnum : IEnumerator
895 private int pos = -1;
897 public MTextEnum (MText mt)
902 public bool MoveNext ()
905 return (pos < mt.nchars);
913 public object Current
916 //if (pos < 0 || pos >= mt.nchars)
917 //throw new InvalidOperationException ();