using System;
using System.Text;
+using System.Collections;
+using System.Collections.Generic;
using M17N.Core;
namespace M17N.Core
}
#endif
+ public class MTextProperty
+ {
+ internal MProperty prop;
+ internal bool front_sticky;
+ internal bool rear_sticky;
+ internal bool merginable;
+ public MProperty Prop { get { return prop; } }
+ public bool FrontSticky { get { return front_sticky; } }
+ public bool RearSticky { get { return rear_sticky; } }
+ public bool Merginable { get { return merginable; } }
+
+ public MTextProperty (bool front_sticky, bool rear_sticky)
+ {
+ this.front_sticky = front_sticky;
+ this.rear_sticky = rear_sticky;
+ }
+ public MTextProperty (bool front_sticky, bool rear_sticky, bool merginable)
+ {
+ this.front_sticky = front_sticky;
+ this.rear_sticky = rear_sticky;
+ this.merginable = merginable;
+ }
+ }
+
public class MText
{
#if false
public enum MTextFormat format;
#endif
- private class MTextPlist : MPlist
- {
- public class MInterval
- {
- MPlist stack;
- int nprops;
- public int start, end;
- public MInterval prev, next;
- }
-
- MInterval head, tail;
-
- public MTextPlist (MText mt)
- {
- head = tail = new MInterval ();
- head.start = 0;
- head.end = mt.sb.Length;
- }
- }
-
private StringBuilder sb;
+ private int nchars;
private int cache_pos;
private int cache_idx;
- private MTextPlist plist;
+ private MInterval root_interval;
+ private bool unmodifiable;
private static UTF8Encoding utf8 = new UTF8Encoding ();
+ private static int count_chars (String str)
+ {
+ int len = str.Length, n = 0;
+
+ for (int i = 0; i < len; i++)
+ n += surrogate_high_p (str[i]) ? 2 : 1;
+ return n;
+ }
+
+ private static int count_chars (StringBuilder str)
+ {
+ int len = str.Length, n = 0;
+
+ for (int i = 0; i < len; i++)
+ n += surrogate_high_p (str[i]) ? 2 : 1;
+ return n;
+ }
+
public MText ()
{
- cache_pos = cache_idx = 0;
- plist = null;
sb = new StringBuilder ();
}
public MText (byte[] str)
{
- cache_pos = cache_idx = 0;
- plist = null;
sb = new StringBuilder (utf8.GetString (str));
+ nchars = count_chars (sb);
}
public MText (String str)
{
- cache_pos = cache_idx = 0;
- plist = null;
sb = new StringBuilder (str);
+ nchars = count_chars (str);
+ }
+
+ public MText (StringBuilder str)
+ {
+ sb = str;
+ nchars = count_chars (str);
+ }
+
+ public static MText operator+ (MText mt1, MText mt2)
+ {
+ MText mt = new MText (mt1.sb);
+
+ mt.sb.Append (mt2.sb);
+ mt.nchars = mt1.nchars + mt2.nchars;
+ return mt;
+ }
+
+ public override string ToString ()
+ {
+ return sb.ToString ();
+ }
+
+ private static bool surrogate_high_p (char c)
+ {
+ return (c >= 0xD800 && c < 0xDC00);
+ }
+
+ private static bool surrogate_low_p (char c)
+ {
+ return (c >= 0xDC00 && c < 0xE000);
+ }
+
+ private static int inc_idx (StringBuilder sb, int i)
+ {
+ return (i + (surrogate_high_p (sb[i]) ? 2 : 1));
+ }
+
+ private static int dec_idx (StringBuilder sb, int i)
+ {
+ return (i - (surrogate_low_p (sb[i - 1]) ? 2 : 1));
+ }
+
+ private static int pos_to_idx (MText mt, int pos)
+ {
+ if (pos == mt.cache_pos)
+ return mt.cache_idx;
+
+ int p, i;
+ bool forward;
+
+ if (pos < mt.cache_pos)
+ {
+ if (mt.cache_pos == mt.cache_idx)
+ return mt.cache_idx;
+ if (pos < mt.cache_pos - pos)
+ {
+ p = i = 0;
+ forward = true;
+ }
+ else
+ {
+ p = mt.cache_pos; i = mt.cache_idx;
+ forward = false;
+ }
+ }
+ else
+ {
+ if (mt.nchars - mt.cache_pos == mt.sb.Length - mt.cache_idx)
+ return (mt.cache_idx + pos - mt.cache_pos);
+ if (pos - mt.cache_pos < mt.nchars - pos)
+ {
+ p = mt.cache_pos; i = mt.cache_idx;
+ forward = true;
+ }
+ else
+ {
+ p = mt.nchars; i = mt.sb.Length;
+ forward = false;
+ }
+ }
+ if (forward)
+ for (; p < pos; i = inc_idx (mt.sb, i), p++);
+ else
+ for (; p > pos; i = dec_idx (mt.sb, i), p--);
+ mt.cache_pos = p;
+ mt.cache_idx = i;
+ return i;
+ }
+
+ private void insert (int pos, MText mt2, int from, int to)
+ {
+ int pos_idx = pos_to_idx (this, pos);
+ int from_idx = pos_to_idx (mt2, from);
+ int to_idx = pos_to_idx (mt2, to);
+
+ sb.Insert (pos_idx, mt2.sb.ToString (from_idx, to_idx - from_idx));
+ nchars += to - from;
}
public int this[int i]
{
- set { this.sb[i] = (char) value; }
- get { return this.sb[i]; }
+ set {
+ i = pos_to_idx (this, i);
+ if (value < 0x10000)
+ {
+ if (surrogate_high_p (sb[i]))
+ sb.Remove (i, 1);
+ sb[i] = (char) value;
+ }
+ else
+ {
+ char high = (char) (0xD800 + ((value - 0x10000) >> 10));
+ char low = (char) (0xDC00 + ((value - 0x10000) & 0x3FF));
+
+ if (! surrogate_high_p (sb[i]))
+ sb.Insert (i, 0);
+ sb[i] = high;
+ sb[i + 1] = low;
+ }
+ }
+ get {
+ i = pos_to_idx (this, i);
+ return (surrogate_high_p (sb[i])
+ ? ((sb[i] - 0xD800) << 10) + (sb[i + 1] - 0xDC00) + 0x10000
+ : sb[i]);
+ }
}
- }
- public class MTextProperty
- {
+ public MText dup ()
+ {
+ return (new MText (sb.ToString ()));
+ }
+
+ public MText ins (int pos, MText mt)
+ {
+ insert (pos, mt, 0, mt.nchars);
+ return this;
+ }
+
+ public MText ins (int pos, MText mt, int from, int to)
+ {
+ insert (pos, mt, from, to);
+ return this;
+ }
+
+ public MText del (int from, int to)
+ {
+ sb.Remove (from, pos_to_idx (this, to) - pos_to_idx (this, from));
+ nchars -= to - from;
+ return this;
+ }
+
+ private class MInterval
+ {
+ // Start and end positions of the MText covered of this interval
+ // and its children. The values are actually (4N +- 1), where N
+ // is a non-negative integer representing a position relative to
+ // the parent interval.
+ private int total_start, total_end;
+ // Stack of MTextProperty
+ private Stack<MTextProperty> stack;
+ // Number of child nodes
+ private int nodes;
+ private MInterval left, right, parent;
+
+ private int Start {
+ get {
+ return (left == null ? total_start : total_start + left.total_end);
+ }
+ }
+
+ private int End {
+ get {
+ return (right == null ? total_end : total_start + right.total_start);
+ }
+ }
+
+ private MInterval Left {
+ get {
+ MInterval interval;
+
+ if (left != null)
+ {
+ for (interval = left; interval.right != null;
+ interval = interval.right);
+ }
+ else
+ {
+ for (interval = parent;
+ interval != null && interval.total_start == total_start;
+ interval = interval.parent);
+ }
+ return interval;
+ }
+ }
+
+ private MInterval Right {
+ get {
+ MInterval interval;
+
+ if (right != null)
+ {
+ for (interval = right; interval.left != null;
+ interval = interval.left);
+ }
+ else
+ {
+ for (interval = parent;
+ interval != null && interval.total_end == total_end;
+ interval = interval.parent);
+ }
+ return interval;
+ }
+ }
+
+ private static int MakePosition (int pos, bool front_inclusive)
+ {
+ return (pos << 2) + (front_inclusive ? -1 : 1);
+ }
+
+ private MInterval (int start, int end)
+ {
+ if (start > end)
+ throw new Exception ("Invalid Interval Range");
+ this.total_start = (start << 2) + 1;
+ this.total_end = (end << 2) + -1;
+ this.stack = new Stack<MTextProperty> ();
+ this.nodes = 1;
+ }
+
+ public MInterval (int start, bool front_inclusive,
+ int end, bool rear_inclusive)
+ {
+ if (start > end)
+ throw new Exception ("Invalid Interval Range");
+ this.total_start = (start << 2) + (front_inclusive ? -1 : 1);
+ this.total_end = (end << 2) + (rear_inclusive ? 1 : -1);
+ this.stack = new Stack<MTextProperty> ();
+ this.nodes = 1;
+ }
+
+ public void Push (MTextProperty prop, int start, int end)
+ {
+ start <<= 2;
+ if (prop.FrontSticky)
+ start--;
+ else
+ start++;
+ end <<= 2;
+ if (prop.RearSticky)
+ end++;
+ else
+ end--;
+ if (start >= end)
+ throw new Exception ("Invalid Text Property Range");
+
+ push (prop, start, end);
+ }
+
+ private MInterval divide_right (int pos)
+ {
+ MInterval interval = new MInterval (pos, End);
+
+ return interval;
+ }
+
+ private MInterval divide_left (int pos)
+ {
+ MInterval interval = new MInterval (Start, pos);
+
+ return interval;
+ }
+
+ private void push (MTextProperty prop, int start, int end)
+ {
+ int this_start = Start;
+ int this_end = End;
+
+ if (start < this_start)
+ {
+ if (end <= this_start)
+ Left.push (prop, start, end);
+ else
+ {
+ Left.push (prop, start, this_start);
+ if (end < this_end)
+ {
+ divide_right (end);
+ stack.Push (prop);
+ }
+ else
+ {
+ stack.Push (prop);
+ if (this_end < end)
+ Right.Push (prop, this_end, end);
+ }
+ }
+ }
+ else if (start < this_end)
+ {
+ divide_right (start).Push (prop, start, end);
+ }
+ else
+ Right.Push (prop, start, end);
+ }
+ }
}
}