using System;
using System.Text;
+using System.Collections;
+using System.Collections.Generic;
using M17N.Core;
namespace M17N.Core
{
#if false
public enum MTextFormat
+ {
+ MTEXT_FORMAT_US_ASCII,
+ MTEXT_FORMAT_UTF_8,
+ MTEXT_FORMAT_UTF_16BE,
+ MTEXT_FORMAT_UTF_16LE,
+ MTEXT_FORMAT_UTF_32BE,
+ MTEXT_FORMAT_UTF_32LE,
+ }
+#endif
+
+ public class MTextProperty
{
- MTEXT_FORMAT_US_ASCII,
- MTEXT_FORMAT_UTF_8,
- MTEXT_FORMAT_UTF_16BE,
- MTEXT_FORMAT_UTF_16LE,
- MTEXT_FORMAT_UTF_32BE,
- MTEXT_FORMAT_UTF_32LE,
+ 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;
+ }
}
-#endif
- public class MText
+ public class MText : IEnumerable, IEquatable<MText>, IComparable<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 bool unmodifiable;
+ private MInterval root_interval;
+ private bool read_only;
private static UTF8Encoding utf8 = new UTF8Encoding ();
{
int len = str.Length, n = 0;
- for (int i = 0; i < len; i++)
- n += surrogate_high_p (str[i]) ? 2 : 1;
+ for (int i = 0; i < len; i++, n++)
+ if (surrogate_high_p (str[i]))
+ i++;
return n;
}
{
int len = str.Length, n = 0;
- for (int i = 0; i < len; i++)
- n += surrogate_high_p (str[i]) ? 2 : 1;
+ for (int i = 0; i < len; i++, n++)
+ if (surrogate_high_p (str[i]))
+ i++;
return n;
}
public MText ()
- {
- sb = new StringBuilder ();
- }
+ {
+ sb = new StringBuilder ();
+ }
public MText (byte[] str)
- {
- sb = new StringBuilder (utf8.GetString (str));
- nchars = count_chars (sb);
- }
+ {
+ sb = new StringBuilder (utf8.GetString (str));
+ nchars = count_chars (sb);
+ }
public MText (String str)
- {
- sb = new StringBuilder (str);
- nchars = count_chars (str);
- }
+ {
+ sb = new StringBuilder (str);
+ nchars = count_chars (str);
+ }
public MText (StringBuilder str)
- {
- sb = str;
- nchars = count_chars (str);
- }
+ {
+ sb = str;
+ nchars = count_chars (str);
+ }
public static MText operator+ (MText mt1, MText mt2)
{
- MText mt = new MText (mt1.sb);
+ MText mt = new MText ();
+ mt.sb.Append (mt1.sb);
mt.sb.Append (mt2.sb);
mt.nchars = mt1.nchars + mt2.nchars;
return mt;
}
+ // Public properties
+ public bool ReadOnly { get { return read_only; } }
+ public int Length { get { return nchars; } }
+
+ // Public methods
+
+ // for IEnumerable interface
+ public IEnumerator GetEnumerator() { return new MTextEnum (this); }
+
+ // for IEquatable interface
+ public bool Equals (MText other) { return this.sb.Equals (other.sb); }
+
+ // for IComparable interface
+ public int CompareTo (MText other)
+ {
+ return this.sb.ToString ().CompareTo (other.sb.ToString ());
+ }
+
+ public override String ToString () { return sb.ToString (); }
+
private static bool surrogate_high_p (char c)
{
return (c >= 0xD800 && c < 0xDC00);
sb.Insert (pos_idx, mt2.sb.ToString (from_idx, to_idx - from_idx));
nchars += to - from;
+ if (root_interval != null)
+ {
+ MInterval interval = (mt2.root_interval == null
+ ? new MInterval (0, false, to - from, false)
+ : mt2.root_interval.CopyTree (from, to));
+ root_interval.Insert (pos, interval);
+ }
}
public int this[int i]
set {
i = pos_to_idx (this, i);
if (value < 0x10000)
- sb[i] = (char) value;
+ {
+ if (surrogate_high_p (sb[i]))
+ sb.Remove (i, 1);
+ sb[i] = (char) value;
+ }
else
{
- char high = (char) (0xD800 + ((i - 0x10000) >> 10));
- char low = (char) (0xDC00 + ((i - 0x10000) & 0x3FF));
+ 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] = low;
+ sb[i] = high;
+ sb[i + 1] = low;
}
}
get {
i = pos_to_idx (this, i);
return (surrogate_high_p (sb[i])
- ? i = ((sb[i] - 0xD800) << 10) + (sb[i + 1] - 0xDC00) + 0x10000
+ ? ((sb[i] - 0xD800) << 10) + (sb[i + 1] - 0xDC00) + 0x10000
: sb[i]);
}
}
public MText dup ()
{
- return (new MText (this.sb));
+ return (new MText (sb.ToString ()));
}
public MText ins (int pos, MText mt)
return this;
}
+ public void PushProp (int from, int to, MTextProperty prop)
+ {
+ if (root_interval == null)
+ root_interval = new MInterval (this);
+ root_interval.Push (from, to, prop);
+ }
- }
+ private class MInterval
+ {
+ // position: 0 1 2 3
+ // index: -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13
+ // | | A | | B | | C | |
+ // interval |<--------->|<--->|<--------------->|<------
+ //
+ // [-1 99 (9 89)]
+ // [0 10 (-1 9)] [-10 0 (89 99)]
+ // [0 4 (-1 3)] [-4 0 (5 9)] [0 4 (89 93)] [-2 0 (97 99)]
+ //
+ // Start and end positions of this interval and its children.
+ // If this is the left node, the values are relative to the
+ // parent's total_start. Otherwise, the values are relatie to
+ // the parent's total_end.
+ private int total_start, total_end;
+ // Stack of MTextProperty
+ private Stack<MTextProperty> stack;
+ private MInterval left, right, parent;
+ private MText mt;
+
+ private static int adjust_position (int pos, bool front_inclusive)
+ {
+ return (pos << 2) + (front_inclusive ? -1 : 1);
+ }
- public class MTextProperty
- {
+ private static bool before_point_p (int pos)
+ {
+ return ((pos + 1) % 4) == 0;
+ }
+
+ 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> ();
+ }
+
+ public MInterval (MText mt)
+ {
+ this.mt = mt;
+ this.total_start = -1;
+ this.total_end = (mt.sb.Length << 2) + 1;
+ this.stack = new Stack<MTextProperty> ();
+ }
+
+ private MInterval () { }
+
+ private MInterval (int start, int end, Stack<MTextProperty> stack)
+ {
+ this.total_start = start;
+ this.total_end = end;
+ this.stack = new Stack<MTextProperty> (stack);
+ }
+
+ private MInterval find (int pos, out int offset)
+ {
+ if (pos < total_start || total_end < pos)
+ {
+ offset = 0;
+ return null;
+ }
+ if (pos < Start)
+ return left.find (pos, out offset);
+ if (End < pos)
+ return right.find (pos - total_end, out offset);
+ offset = pos - Start;
+ return this;
+ }
+
+ // p-. or .-p p-. or .-p
+ // .-this-. .-right-.
+ // left .-right-. -> .-this-. c2
+ // c1 c2 left c1
+ private MInterval promote_right ()
+ {
+ int right_length = - right.total_start;
+ MInterval c1;
+
+ if (parent == null)
+ mt.root_interval = right;
+ else if (total_start == 0)
+ parent.left = right;
+ else
+ parent.right = right;
+ right.parent = parent;
+ c1 = right.left;
+ right.left = this;
+
+ parent = right;
+ right = c1;
+ if (c1 != null)
+ c1.parent = this;
+
+ parent.total_start = total_start;
+ parent.total_end = total_end;
+ total_start = 0;
+ total_end -= right_length - (c1 == null ? 0 : c1.total_end);
+
+ if (c1 != null)
+ {
+ c1.total_end = - c1.total_start;
+ c1.total_start = 0;
+ }
+ return parent;
+ }
+
+ // p-. or .-p p-. or .-p
+ // .-this-. .-left-.
+ // .-left-. .-right-. -> c1 .-this-.
+ // c1 c2 c2 right
+ private MInterval promote_left ()
+ {
+ int left_length = left.total_end;
+ MInterval c2;
+
+ if (parent == null)
+ mt.root_interval = left;
+ else if (total_start == 0)
+ parent.left = left;
+ else
+ parent.right = left;
+ left.parent = parent;
+ c2 = left.right;
+ left.right = this;
+
+ parent = left;
+ left = c2;
+ if (c2 != null)
+ c2.parent = this;
+
+ parent.total_start = total_start;
+ parent.total_end = total_end;
+ total_start -= left_length + (c2 == null ? 0 : c2.total_end);;
+ total_end = 0;
+
+ if (c2 != null)
+ {
+ c2.total_start = - c2.total_end;
+ c2.total_end = 0;
+ }
+ return parent;
+ }
+
+ private MInterval balance ()
+ {
+ MInterval interval = this;
+
+ while (true)
+ {
+ int this_start = interval.Start;
+ int this_end = interval.End;
+ int diff = ((this_start - interval.total_start)
+ - (interval.total_end - this_end));
+ int abs = Math.Abs (diff);
+
+ if (abs < this_end - this_start)
+ break;
+ if (diff < 0)
+ {
+ interval = interval.promote_right ();
+ interval.left.balance ();
+ }
+ else
+ {
+ interval = interval.promote_left ();
+ interval.right.balance ();
+ }
+ }
+ return interval;
+ }
+
+ public MInterval CopyTree (int start, int end)
+ {
+ MInterval interval_start, interval_end, interval;
+ int offset_start, offset_end;
+
+ start <<= 2;
+ end <<= 2;
+ interval_start = find (start, out offset_start);
+ interval_end = find (end, out offset_end);
+
+ interval = new MInterval ();
+ interval.total_start = 0;
+ interval.total_end = end - start;
+ interval.stack = new Stack<MTextProperty> (interval_start.stack);
+ interval = interval.divide_right (offset_start);
+ while (interval_start != interval_end)
+ {
+ interval_start = interval_start.Right;
+ interval = interval.divide_right (interval_start.End
+ - interval_start.Start);
+ }
+ return interval;
+ }
+
+ private MInterval CopyNode ()
+ {
+ return new MInterval (total_start, total_end, stack);
+ }
+
+ private int Start {
+ get {
+ return (left == null ? total_start : total_start + left.total_end);
+ }
+ }
+
+ private int End {
+ get {
+ return (right == null ? total_end : total_end + 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 == 0;
+ 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_start < 0;
+ interval = interval.parent);
+
+ return interval;
+ }
+ }
+
+ public void Push (int start, int end, MTextProperty prop)
+ {
+ 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 (start, end, prop);
+ }
+
+ public void Insert (int pos, MInterval interval)
+ {
+ if (pos < Start)
+ Left.Insert (pos - total_start, interval);
+ else if (pos > End)
+ Right.Insert (pos - total_end, interval);
+ else
+ {
+ // position: 0 1 2 3
+ // index: -1 0 1 2 3 4 5 6 7 8 9 10 11 12 13
+ // this |<----------<-------------->->|
+ // |<--------->| |<>|
+ //
+ // interval |<->----->->|
+ //
+ // new |<----------<----->----->-->------------>->|
+ // |<--------->| |<---->-->------------>->|
+ // |<----------->->|
+ // |<>|
+ int len = interval.total_end - interval.total_start;
+ MInterval temp;
+
+ total_end += len;
+ for (temp = this; temp.parent != null;
+ temp = temp.parent)
+ temp.parent.total_end += len;
+ if (End - pos < interval.End)
+ {
+
+
+ }
+
+ temp = divide_right (Start + 2);
+ temp.left = interval;
+ right = interval;
+ }
+
+
+ }
+
+ private MInterval divide_right (int pos)
+ {
+ MInterval interval = CopyNode ();
+ int this_start = Start;
+ int this_end = End;
+
+ if (left == null
+ || (right != null && left.total_end < - right.total_start))
+ {
+ interval.left = this;
+ interval.right = right;
+ interval.parent = parent;
+ if (parent != null)
+ {
+ if (total_start == 0)
+ parent.left = interval;
+ else
+ parent.right = interval;
+ }
+ total_start = 0;
+ total_end = pos - this_start;
+ }
+ else
+ {
+ interval.total_start = pos - this_end;
+ interval.total_end = 0;
+ if (right != null)
+ right.parent = interval;
+ right = interval;
+ }
+
+ return interval;
+ }
+
+ private MInterval divide_left (int pos)
+ {
+ MInterval interval = CopyNode ();
+ int this_start = Start;
+ int this_end = End;
+
+ if (left == null
+ || (right != null && left.total_end < - right.total_start))
+ {
+ interval.total_start = 0;
+ interval.total_end = pos - this_start;
+ if (left != null)
+ left.parent = interval;
+ left = interval;
+ }
+ else
+ {
+ interval.right = this;
+ interval.left = left;
+ interval.parent = parent;
+ if (parent != null)
+ {
+ if (total_start == 0)
+ parent.left = interval;
+ else
+ parent.right = interval;
+ }
+ total_start = pos - this_end;
+ total_end = 0;
+ }
+
+ return interval;
+ }
+
+ private void push (int start, int end, MTextProperty prop)
+ {
+ int this_start = Start;
+ int this_end = End;
+
+ if (start < this_start)
+ {
+ if (end <= this_start)
+ {
+ Left.push (start, end, prop);
+ return;
+ }
+ Left.push (start, this_start, prop);
+ start = this_start;
+ }
+ if (this_end < end)
+ {
+ if (this_end < start)
+ {
+ Right.push (start, end, prop);
+ return;
+ }
+ Right.push (this_end, end, prop);
+ end = this_end;
+ }
+ if (this_start < start)
+ divide_left (start);
+ if (end < this_end)
+ divide_right (end);
+ stack.Push (prop);
+ }
+ }
+
+ private class MTextEnum : IEnumerator
+ {
+ private MText mt;
+ private int pos = -1;
+
+ public MTextEnum (MText mt)
+ {
+ this.mt = mt;
+ }
+
+ public bool MoveNext ()
+ {
+ pos++;
+ return (pos < mt.nchars);
+ }
+
+ public void Reset ()
+ {
+ pos = -1;
+ }
+
+ public object Current
+ {
+ get {
+ //if (pos < 0 || pos >= mt.nchars)
+ //throw new InvalidOperationException ();
+ return mt[pos];
+ }
+ }
+ }
}
}