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 { 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 StringBuilder sb; private int nchars; private int cache_pos; private int cache_idx; 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 () { sb = new StringBuilder (); } public MText (byte[] str) { sb = new StringBuilder (utf8.GetString (str)); nchars = count_chars (sb); } public MText (String str) { 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 { 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 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 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 (); 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 (); 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); } } } }