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;
71 public override string ToString ()
73 return key.ToString () + ":" + val;
77 public class MText : IEnumerable, IEquatable<MText>, IComparable<MText>
80 public enum MTextFormat format;
82 private StringBuilder sb;
84 private int cache_pos;
85 private int cache_idx;
86 private MPlist intervals;
87 private MPlist default_property;
88 private bool read_only;
90 private static UTF8Encoding utf8 = new UTF8Encoding ();
92 private static int count_chars (String str)
94 int len = str.Length, n = 0;
96 for (int i = 0; i < len; i++, n++)
97 if (surrogate_high_p (str[i]))
102 private static int count_chars (StringBuilder str)
104 int len = str.Length, n = 0;
106 for (int i = 0; i < len; i++, n++)
107 if (surrogate_high_p (str[i]))
114 sb = new StringBuilder ();
115 intervals = new MPlist ();
118 public MText (byte[] str)
120 sb = new StringBuilder (utf8.GetString (str));
121 nchars = count_chars (sb);
122 intervals = new MPlist ();
125 public MText (String str)
127 sb = new StringBuilder (str);
128 nchars = count_chars (str);
129 intervals = new MPlist ();
132 public MText (StringBuilder str)
135 nchars = count_chars (str);
136 intervals = new MPlist ();
139 public static MText operator+ (MText mt1, MText mt2)
141 MText mt = new MText ();
143 mt.sb.Append (mt1.sb);
144 mt.sb.Append (mt2.sb);
145 mt.nchars = mt1.nchars + mt2.nchars;
150 public bool ReadOnly { get { return read_only; } }
151 public int Length { get { return nchars; } }
155 // for IEnumerable interface
156 public IEnumerator GetEnumerator() { return new MTextEnum (this); }
158 // for IEquatable interface
159 public bool Equals (MText other) { return this.sb.Equals (other.sb); }
161 // for IComparable interface
162 public int CompareTo (MText other)
164 return this.sb.ToString ().CompareTo (other.sb.ToString ());
167 public override String ToString () { return "\"" + sb.ToString () + "\""; }
169 private static bool surrogate_high_p (char c)
171 return (c >= 0xD800 && c < 0xDC00);
174 private static bool surrogate_low_p (char c)
176 return (c >= 0xDC00 && c < 0xE000);
179 private static int inc_idx (StringBuilder sb, int i)
181 return (i + (surrogate_high_p (sb[i]) ? 2 : 1));
184 private static int dec_idx (StringBuilder sb, int i)
186 return (i - (surrogate_low_p (sb[i - 1]) ? 2 : 1));
189 private static int pos_to_idx (MText mt, int pos)
191 if (pos == mt.cache_pos)
197 if (pos < mt.cache_pos)
199 if (mt.cache_pos == mt.cache_idx)
201 if (pos < mt.cache_pos - pos)
208 p = mt.cache_pos; i = mt.cache_idx;
214 if (mt.nchars - mt.cache_pos == mt.sb.Length - mt.cache_idx)
215 return (mt.cache_idx + pos - mt.cache_pos);
216 if (pos - mt.cache_pos < mt.nchars - pos)
218 p = mt.cache_pos; i = mt.cache_idx;
223 p = mt.nchars; i = mt.sb.Length;
228 for (; p < pos; i = inc_idx (mt.sb, i), p++);
230 for (; p > pos; i = dec_idx (mt.sb, i), p--);
236 private void check_pos (int pos, bool tail_ok)
238 if (pos < 0 || (tail_ok ? pos > nchars : pos >= nchars))
239 throw new Exception ("Invalid MText position:" + pos);
242 private bool check_range (int from, int to, bool zero_ok)
244 if (from < 0 || (zero_ok ? from > to : from >= to)
246 throw new Exception ("Invalid MText range");
250 private void insert (int pos, MText mt2, int from, int to)
252 check_pos (pos, true);
254 int pos_idx = pos_to_idx (this, pos);
255 int from_idx = pos_to_idx (mt2, from);
256 int to_idx = pos_to_idx (mt2, to);
258 sb.Insert (pos_idx, mt2.sb.ToString (from_idx, to_idx - from_idx));
261 foreach (MPlist plist in mt2.intervals)
262 if (intervals.Find (plist.Key) == null)
263 intervals.Push (plist.Key, new MInterval (plist.Key, this));
264 foreach (MPlist plist in intervals)
266 MPlist p = mt2.intervals.Find (plist.Key);
270 interval = new MInterval (plist.Key, this, to - from);
272 interval = ((MInterval) p.Val).Copy (from, to);
273 ((MInterval) plist.Val).Insert (pos, interval);
277 public int this[int i]
280 i = pos_to_idx (this, i);
283 if (surrogate_high_p (sb[i]))
285 sb[i] = (char) value;
289 char high = (char) (0xD800 + ((value - 0x10000) >> 10));
290 char low = (char) (0xDC00 + ((value - 0x10000) & 0x3FF));
292 if (! surrogate_high_p (sb[i]))
299 i = pos_to_idx (this, i);
300 return (surrogate_high_p (sb[i])
301 ? ((sb[i] - 0xD800) << 10) + (sb[i + 1] - 0xDC00) + 0x10000
308 return (new MText (sb.ToString ()));
311 public MText Ins (int pos, MText mt)
313 insert (pos, mt, 0, mt.nchars);
317 public MText Ins (int pos, MText mt, int from, int to)
319 insert (pos, mt, from, to);
323 public MText Del (int from, int to)
325 if (check_range (from, to, true))
328 sb.Remove (from, pos_to_idx (this, to) - pos_to_idx (this, from));
332 foreach (MPlist plist in intervals)
333 ((MInterval) plist.Val).Delete (from, to);
335 intervals = new MPlist ();
339 public object GetProp (int pos, MSymbol key)
341 check_pos (pos, false);
343 MInterval i = (MInterval) intervals.Find (key).Val;
348 MTextProperty prop = i.Get (pos);
349 return (prop != null ? prop.Val : null);
352 public object GetProp (int pos, MSymbol key, out MTextProperty prop)
354 check_pos (pos, false);
356 MInterval i = (MInterval) intervals.Find (key).Val;
359 return (prop = null);
361 return (prop != null ? prop.Val : null);
364 public object GetProp (int pos, MSymbol key, out MTextProperty[] array)
366 check_pos (pos, false);
368 MInterval i = (MInterval) intervals.Find (key).Val;
371 return (array = null);
372 MTextProperty prop = i.Get (pos, out array);
373 return (prop != null ? prop.Val : null);
376 public void PushProp (int from, int to, MSymbol key, object val)
378 if (! check_range (from, to, true))
379 PushProp (from, to, new MTextProperty (key, val));
382 public void PushProp (int from, int to, MTextProperty prop)
386 if (default_property == null)
387 default_property = new MPlist ();
388 default_property.Push (prop.key, prop.val);
392 if (check_range (from, to, true))
395 MPlist p = intervals.Find (prop.key);
400 root = new MInterval (prop.key, this);
401 intervals.Push (prop.key, root);
404 root = (MInterval) p.Val;
406 root.Push (from, to, prop);
410 public void PopProp (int from, int to, MSymbol key)
414 if (default_property == null)
416 MPlist p = default_property.Find (key);
423 if (check_range (from, to, true))
426 MPlist p = intervals.Find (key);
429 ((MInterval) p.Val).Pop (from, to);
433 public void DumpProp ()
436 foreach (MPlist p in intervals)
437 ((MInterval) p.Val).Dump ();
438 Console.WriteLine (")");
441 private class MInterval
443 // position: 0 1 2 3 4 5 6 7
444 // | A | B | C | D | E F | G |
445 // interval |---|---|---|<->|-------|---|
446 // |---|<->|---| |<----->|---|
450 // [3 (1 2)] [3 (4 6)]
451 // [1 (0 1)] [2 (2 3)] [1 (6 7)]
453 private static int count = 0;
456 private int From, To;
458 private MPlist Stack;
459 private MInterval Left, Right, Parent;
462 public MInterval (MSymbol key, MText mt, int length)
465 throw new Exception ("Invalid interval length");
469 Stack = new MPlist ();
473 public MInterval (MSymbol key, MText mt)
477 Length = mt.sb.Length;
480 Stack = new MPlist ();
484 public MTextProperty Get (int pos)
486 MInterval i = find (pos);
488 return (i.Stack.IsEmpty ? null : (MTextProperty) i.Stack.Val);
491 public MTextProperty Get (int pos, out MTextProperty[] array)
493 MInterval i = find (pos);
500 array = new MTextProperty[i.Stack.Count];
504 for (idx = 0, p = i.Stack; ! p.IsEmpty; idx++, p = p.Next)
505 array[idx] = (MTextProperty) p.Val;
506 return array[idx - 1];
509 private MInterval (MSymbol key, MText mt, int length, MPlist stack)
516 Stack = stack.Clone ();
520 private void update_from_to ()
525 To = Length - RightLength;
527 else if (Parent.Left == this)
529 From = Parent.From - Length + LeftLength;
530 To = Parent.From - RightLength;
534 From = Parent.To + LeftLength;
535 To = Parent.To + Length - RightLength;
539 private int LeftLength
541 get { return (Left == null ? 0 : Left.Length); }
544 private int RightLength
546 get { return (Right == null ? 0 : Right.Length); }
549 private MInterval LeftMost
551 get { return (Left == null ? this : Left.LeftMost); }
554 private MInterval RightMost
556 get { return (Right == null ? this : Right.RightMost); }
559 private MInterval Prev {
564 for (i = Left; i.Right != null; i = i.Right);
567 MInterval child = this;
568 for (i = Parent; i != null && i.Left == child;
569 child = i, i = i.Parent);
575 private MInterval Next {
580 for (i = Right; i.Left != null; i = i.Left);
583 MInterval child = this;
584 for (i = Parent; i != null && i.Right == child;
585 child = i, i = i.Parent);
591 private MInterval find (int pos)
595 return Left.find (pos);
597 return Right.find (pos);
601 private bool mergeable (MInterval i)
605 for (p1 = Stack, p2 = i.Stack; ! p1.IsEmpty && ! p2.IsEmpty;
606 p1 = p1.Next, p2 = p2.Next)
607 if (p1.Val != p2.Val)
609 return (p1.IsEmpty && p2.IsEmpty);
612 // p-. or .-p p-. or .-p
613 // .-this-. .-right-.
614 // left .-right-. -> .-this-. c2
616 private MInterval promote_right ()
618 int right_length = Right.Length;
622 mtext.intervals.Put (Key, Right);
623 else if (Parent.Left == this)
626 Parent.Right = Right;
627 Right.Parent = Parent;
633 Parent.Length += Length;
634 Length -= right_length;
638 Parent.Length -= c1.Length;
644 // p-. or .-p p-. or .-p
646 // .-left-. .-right-. -> c1 .-this-.
648 private MInterval promote_left ()
650 int left_length = Left.Length;
654 mtext.intervals.Put (Key, Left);
655 else if (Parent.Left == this)
659 Left.Parent = Parent;
665 Parent.Length += Length;
666 Length -= left_length;
670 Parent.Length -= c1.Length;
676 private MInterval balance ()
683 // .-left-. .-right-.
685 int diff = i.LeftLength - i.RightLength;
690 new_diff = (i.Length - i.LeftLength
691 + i.Left.RightLength - i.Left.LeftLength);
692 if (Math.Abs (new_diff) >= diff)
694 i = i.promote_left ();
699 new_diff = (i.Length - i.RightLength
700 + i.Right.LeftLength - i.Right.RightLength);
701 if (Math.Abs (new_diff) >= diff)
703 i = i.promote_right ();
710 public MInterval Copy (int start, int end)
712 MInterval copy, left_copy = null, right_copy = null;
718 return Left.Copy (start, end);
719 left_copy = Left.Copy (start, From);
724 return Right.Copy (start, end);
725 right_copy = Right.Copy (To, end);
728 copy = new MInterval (Key, null, end - start, Stack);
729 remove_properties (MTextProperty.Flag.Sensitive);
730 copy.Left = left_copy;
731 copy.Right = right_copy;
739 private MInterval divide_right (int pos)
741 MInterval interval = new MInterval (Key, mtext, To - pos, Stack);
743 Console.Write ("divide-right({0}) at ", pos); DumpOne (false, true);
747 interval.Right = Right;
748 Right.Parent = interval;
749 interval.Length += Right.Length;
751 interval.Parent = this;
759 private MInterval divide_left (int pos)
761 MInterval interval = new MInterval (Key, mtext, pos - From, Stack);
763 Console.Write ("divide-left({0}) at ", pos); DumpOne (false, true);
767 interval.Left = Left;
768 Left.Parent = interval;
769 interval.Length += Left.Length;
771 interval.Parent = this;
776 private void remove_properties (MTextProperty.Flag flags)
778 for (MPlist p = Stack; ! p.IsEmpty;)
780 MTextProperty prop = (MTextProperty) p.Val;
782 if ((prop.flags & flags) == flags)
789 private void inherit_front_properties (MPlist plist)
791 for (MInterval i = LeftMost; i != null; i = i.Next)
795 for (MPlist p = plist; ! p.IsEmpty; p = p.Next)
797 MTextProperty prop = (MTextProperty) p.Val;
799 if ((prop.flags & MTextProperty.Flag.RearSticky)
800 == MTextProperty.Flag.RearSticky)
801 i.Stack.Add (prop.key, prop);
806 private void inherit_rear_properties (MPlist plist)
808 for (MInterval i = RightMost; i != null; i = i.Prev)
812 for (MPlist p = plist; ! p.IsEmpty; p = p.Next)
814 MTextProperty prop = (MTextProperty) p.Val;
816 if ((prop.flags & MTextProperty.Flag.FrontSticky)
817 == MTextProperty.Flag.FrontSticky)
818 i.Stack.Add (prop.key, prop);
823 private MInterval delete_node_forward ()
827 int len = Length - RightLength;
829 for (MInterval i = Parent; i != null; i = i.Parent)
831 if (Parent.Left == this)
834 Parent.Right = Right;
839 Right.Parent = Parent;
840 return Right.LeftMost;
845 private MInterval delete_node_backward ()
849 int len = Length - RightLength;
851 for (MInterval i = Parent; i != null; i = i.Parent)
853 if (Parent.Left == this)
861 Left.Parent = Parent;
862 return Left.RightMost;
867 private void set_mtext (MText mt)
873 Right.set_mtext (mt);
876 private MInterval graft (MInterval interval, bool forward, out int len)
883 i = interval.LeftMost;
888 len += i.Length - i.RightLength;
889 i = i.delete_node_forward ();
894 i = interval.RightMost;
899 len += i.Length - i.LeftLength;
900 i = i.delete_node_backward ();
906 for (MInterval prev = this, ii = this.Parent; ii != null;
907 prev = ii, ii = ii.Parent)
917 while (i.Parent != null) i = i.Parent;
921 public void Insert (int pos, MInterval interval)
924 Console.Write ("insert({0}) at {1} in ", interval.Length, pos);
925 DumpOne (false, true);
927 interval.set_mtext (mtext);
930 Prev.Insert (pos, interval);
931 else if (pos == From)
933 MInterval prev = Prev;
939 prev.Insert (pos, interval);
942 prev.remove_properties
943 (MTextProperty.Flag.Sensitive|MTextProperty.Flag.RearSticky);
944 interval.inherit_front_properties (prev.Stack);
947 (MTextProperty.Flag.Sensitive|MTextProperty.Flag.FrontSticky);
948 interval.inherit_rear_properties (Stack);
951 interval = graft (interval, false, out len);
952 if (interval != null && prev != null)
953 interval = prev.graft (interval, true, out len);
954 if (interval != null)
960 // .-this-. ==> .-this-.
973 for (; i != null; i = i.Parent)
974 i.Length += interval.Length;
979 remove_properties (MTextProperty.Flag.Sensitive);
982 interval = graft (interval, true, out len);
984 if (interval != null)
985 interval = graft (interval, false, out len);
986 if (interval != null)
989 Right.Left = interval;
990 interval.Parent = Right;
991 for (MInterval i = Right; i != null; i = i.Parent)
992 i.Length += interval.Length;
997 MInterval next = Next;
1003 next.Insert (pos, interval);
1006 next.remove_properties
1007 (MTextProperty.Flag.Sensitive|MTextProperty.Flag.FrontSticky);
1008 interval.inherit_rear_properties (next.Stack);
1011 (MTextProperty.Flag.Sensitive|MTextProperty.Flag.RearSticky);
1012 interval.inherit_front_properties (Stack);
1015 interval = graft (interval, true, out len);
1016 if (interval != null && next != null)
1017 interval = next.graft (interval, false, out len);
1018 if (interval != null)
1024 // .-this-. ==> .-this-.
1037 interval.Parent = i;
1038 for (; i != null; i = i.Parent)
1039 i.Length += interval.Length;
1043 Next.Insert (pos, interval);
1046 private void vacate_node (MInterval interval)
1048 Console.WriteLine ("vacate #{0} to #{1}", ID, interval.ID);
1049 if (interval != null)
1050 interval.Parent = Parent;
1054 mtext.intervals.Put (Key, interval);
1058 if (this == Parent.Right)
1059 Parent.Right = interval;
1061 Parent.Left = interval;
1064 if (interval != null)
1065 diff -= interval.Length;
1066 for (MInterval i = Parent; i != null; i = i.Parent)
1071 public void Delete (int start, int end)
1074 Console.Write ("delete({0} {1}) from ", start, end); DumpOne (false, true);
1079 Left.Delete (start, end);
1082 Left.Delete (start, From);
1084 end -= From - start;
1091 Right.Delete (start, end);
1094 Right.Delete (To, end);
1097 if (start == From && end == To)
1109 for (i = Right; i.Left != null; i = i.Left)
1110 i.Length += Left.Length;
1111 i.Length += Left.Length;
1115 vacate_node (Right);
1120 int len = end - start;
1122 for (MInterval i = this; i != null; i = i.Parent)
1127 public void Push (int start, int end, MTextProperty prop)
1130 Console.Write ("push({0} {1}) at ", start, end); DumpOne (false, true);
1135 Left.Push (start, end, prop);
1138 Left.Push (start, From, prop);
1145 Right.Push (start, end, prop);
1148 Right.Push (To, end, prop);
1153 divide_left (start);
1156 Stack.Push (prop.key, prop);
1159 private bool try_merge_prev ()
1161 MInterval prev = Prev;
1163 if (! mergeable (prev))
1166 int len = prev.Length - prev.LeftLength;
1168 // PREV is Left, Left.Right, ..., or Left....Right.
1171 prev.Parent.Right = prev.Left;
1172 while (prev.Parent != Left)
1179 if (Left.Length == Left.LeftLength)
1184 private bool try_merge_next ()
1186 MInterval next = Next;
1188 if (! mergeable (next))
1191 int len = next.Length - next.LeftLength;
1193 // NEXT is Right, Right.Left, ..., or Right....Left.
1196 next.Parent.Left = next.Right;
1197 while (next.Parent != Right)
1203 Right.Length -= len;
1204 if (Right.Length == Right.LeftLength)
1211 public void Pop (int start, int end)
1214 Console.Write ("pop({0} {1}) at ", start, end); DumpOne (false, true);
1219 Left.Pop (start, end);
1222 Left.Pop (start, From);
1229 Right.Pop (start, end);
1232 Right.Pop (To, end);
1236 if (! Stack.IsEmpty)
1238 bool check_prev = start == From && start > 0;
1239 bool check_next = end == To && end < mtext.Length;
1242 divide_left (start);
1246 if (check_prev && Left != null)
1247 check_prev = try_merge_prev () && (Left != null);
1248 if (check_next && Right != null)
1249 check_next = try_merge_next () && (Right != null);
1252 if (Prev.try_merge_next () && check_next)
1253 Prev.try_merge_next ();
1255 else if (check_next)
1257 Next.try_merge_prev ();
1262 private void DumpOne (bool with_prop, bool newline)
1264 Console.Write ("#{0}({1} {2} {3}", ID, Length, From, To);
1266 foreach (MPlist p in Stack)
1267 Console.Write (" " + p.Val);
1268 Console.Write (")");
1270 Console.WriteLine ();
1280 Console.Write (" ");
1281 DumpOne (true, false);
1287 private class MTextEnum : IEnumerator
1290 private int pos = -1;
1292 public MTextEnum (MText mt)
1297 public bool MoveNext ()
1300 return (pos < mt.nchars);
1303 public void Reset ()
1308 public object Current
1311 //if (pos < 0 || pos >= mt.nchars)
1312 //throw new InvalidOperationException ();