*** empty log message ***
[m17n/m17n-lib-cs.git] / MText.cs
1 using System;
2 using System.Text;
3 using System.Collections;
4 using System.Collections.Generic;
5 using M17N.Core;
6
7 namespace M17N.Core
8 {
9 #if false
10   public enum MTextFormat
11   {
12     MTEXT_FORMAT_US_ASCII,
13     MTEXT_FORMAT_UTF_8,
14     MTEXT_FORMAT_UTF_16BE,
15     MTEXT_FORMAT_UTF_16LE,
16     MTEXT_FORMAT_UTF_32BE,
17     MTEXT_FORMAT_UTF_32LE,
18   }
19 #endif
20
21   public class MTextProperty
22   {
23     internal MProperty prop;
24     internal bool front_sticky;
25     internal bool rear_sticky;
26     internal bool merginable;
27     public MProperty Prop { get { return prop; } }
28     public bool FrontSticky { get { return front_sticky; } }
29     public bool RearSticky { get { return rear_sticky; } }
30     public bool Merginable { get { return merginable; } }
31
32     public MTextProperty (bool front_sticky, bool rear_sticky)
33     {
34       this.front_sticky = front_sticky;
35       this.rear_sticky = rear_sticky;
36     }
37     public MTextProperty (bool front_sticky, bool rear_sticky, bool merginable)
38     {
39       this.front_sticky = front_sticky;
40       this.rear_sticky = rear_sticky;
41       this.merginable = merginable;
42     }
43   }
44
45   public class MText
46   {
47 #if false
48     public enum MTextFormat format;
49 #endif
50
51     private StringBuilder sb;
52     private int nchars;
53     private int cache_pos;
54     private int cache_idx;
55     private MInterval root_interval;
56     private bool read_only;
57
58     private static UTF8Encoding utf8 = new UTF8Encoding ();
59
60     private static int count_chars (String str)
61     {
62       int len = str.Length, n = 0;
63
64       for (int i = 0; i < len; i++) 
65         n += surrogate_high_p (str[i]) ? 2 : 1;
66       return n;
67     }
68
69     private static int count_chars (StringBuilder str)
70     {
71       int len = str.Length, n = 0;
72
73       for (int i = 0; i < len; i++) 
74         n += surrogate_high_p (str[i]) ? 2 : 1;
75       return n;
76     }
77
78     public MText ()
79     {
80       sb = new StringBuilder ();
81     }
82
83     public MText (byte[] str)
84     {
85       sb = new StringBuilder (utf8.GetString (str));
86       nchars = count_chars (sb);
87     }
88
89     public MText (String str)
90     {
91       sb = new StringBuilder (str);
92       nchars = count_chars (str);
93     }
94
95     public MText (StringBuilder str)
96     {
97       sb = str;
98       nchars = count_chars (str);
99     }
100
101     public static MText operator+ (MText mt1, MText mt2)
102     {
103       MText mt = new MText (mt1.sb.ToString);
104
105       mt.sb.Append (mt2.sb);
106       mt.nchars = mt1.nchars + mt2.nchars;
107       return mt;
108     }
109
110     public bool ReadOnly { get { return read_only; } }
111
112     public override string ToString ()
113     {
114       return sb.ToString ();
115     }
116
117     private static bool surrogate_high_p (char c)
118     {
119       return (c >= 0xD800 && c < 0xDC00);
120     }
121
122     private static bool surrogate_low_p (char c)
123     {
124       return (c >= 0xDC00 && c < 0xE000);
125     }
126
127     private static int inc_idx (StringBuilder sb, int i)
128     {
129       return (i + (surrogate_high_p (sb[i]) ? 2 : 1));
130     }
131
132     private static int dec_idx (StringBuilder sb, int i)
133     {
134       return (i - (surrogate_low_p (sb[i - 1]) ? 2 : 1));
135     }
136
137     private static int pos_to_idx (MText mt, int pos)
138     {
139       if (pos == mt.cache_pos)
140         return mt.cache_idx;
141
142       int p, i;
143       bool forward;
144
145       if (pos < mt.cache_pos)
146         {
147           if (mt.cache_pos == mt.cache_idx)
148             return mt.cache_idx;
149           if (pos < mt.cache_pos - pos)
150             {
151               p = i = 0;
152               forward = true;
153             }
154           else
155             {
156               p = mt.cache_pos; i = mt.cache_idx;
157               forward = false;
158             }
159         }
160       else
161         {
162           if (mt.nchars - mt.cache_pos == mt.sb.Length - mt.cache_idx)
163             return (mt.cache_idx + pos - mt.cache_pos);
164           if (pos - mt.cache_pos < mt.nchars - pos)
165             {
166               p = mt.cache_pos; i = mt.cache_idx;
167               forward = true;
168             }
169           else
170             {
171               p = mt.nchars; i = mt.sb.Length;
172               forward = false;
173             }
174         }
175       if (forward)
176         for (; p < pos; i = inc_idx (mt.sb, i), p++);
177       else
178         for (; p > pos; i = dec_idx (mt.sb, i), p--);
179       mt.cache_pos = p;
180       mt.cache_idx = i;
181       return i;
182     }
183
184     private void insert (int pos, MText mt2, int from, int to)
185     {
186       int pos_idx = pos_to_idx (this, pos);
187       int from_idx = pos_to_idx (mt2, from);
188       int to_idx = pos_to_idx (mt2, to);
189
190       sb.Insert (pos_idx, mt2.sb.ToString (from_idx, to_idx - from_idx));
191       nchars += to - from;
192     }
193
194     public int this[int i]
195     {
196       set {
197         i = pos_to_idx (this, i);
198         if (value < 0x10000)
199           {
200             if (surrogate_high_p (sb[i]))
201               sb.Remove (i, 1);
202             sb[i] = (char) value;
203           }
204         else
205           {
206             char high = (char) (0xD800 + ((value - 0x10000) >> 10));
207             char low = (char) (0xDC00 + ((value - 0x10000) & 0x3FF));
208
209             if (! surrogate_high_p (sb[i]))
210               sb.Insert (i, 0);
211             sb[i] = high;
212             sb[i + 1] = low;
213           }
214       }
215       get {
216         i = pos_to_idx (this, i);
217         return (surrogate_high_p (sb[i])
218                 ? ((sb[i] - 0xD800) << 10) + (sb[i + 1] - 0xDC00) + 0x10000
219                 : sb[i]);
220       }
221     }
222
223     public MText dup ()
224     {
225       return (new MText (sb.ToString ()));
226     }
227
228     public MText ins (int pos, MText mt)
229     {
230       insert (pos, mt, 0, mt.nchars);
231       return this;
232     }
233
234     public MText ins (int pos, MText mt, int from, int to)
235     {
236       insert (pos, mt, from, to);
237       return this;
238     }
239
240     public MText del (int from, int to)
241     {
242       sb.Remove (from, pos_to_idx (this, to) - pos_to_idx (this, from));
243       nchars -= to - from;
244       return this;
245     }
246
247     public void PushProp (int from, int to, MTextProperty prop)
248     {
249       if (root_interval == null)
250         root_interval = new MInterval (0, true, sb.Length, true);
251       root_interval.Push (from, to, prop);
252     }
253
254     private class MInterval
255     {
256       // Start and end positions of this interval and its children.
257       // If this is the left node, the values are relative to the
258       // parent's total_start.  Otherwise, the values are relatie to
259       // the parent's total_end.
260       private int total_start, total_end;
261       // Stack of MTextProperty
262       private Stack<MTextProperty> stack;
263       private int depth;
264       private MInterval left, right, parent;
265
266       public MInterval (int start, bool front_inclusive,
267                         int end, bool rear_inclusive)
268       {
269         if (start > end)
270           throw new Exception ("Invalid Interval Range");
271         this.total_start = (start << 2) + (front_inclusive ? -1 : 1);
272         this.total_end = (end << 2) + (rear_inclusive ? 1 : -1);
273         this.stack = new Stack<MTextProperty> ();
274         this.depth = 1;
275       }
276
277       private MInterval (int start, int end, Stack<MTextProperty> stack)
278       {
279         this.total_start = start;
280         this.total_end = end;
281         this.stack = new Stack<MTextProperty> (stack);
282         this.depth = 1;
283       }
284
285       private MInterval Copy ()
286       {
287         return new MInterval (total_start, total_end, stack);
288       }
289
290       private int Start {
291         get {
292           return (left == null ? total_start : total_start + left.total_end);
293         }
294       }
295
296       private int End {
297         get {
298           return (right == null ? total_end : total_end + right.total_start);
299         }
300       }
301
302       private MInterval Left {
303         get {
304           MInterval interval;
305
306           if (left != null)
307             for (interval = left;
308                  interval.right != null;
309                  interval = interval.right);
310           else
311             for (interval = parent;
312                  interval != null && interval.total_start == 0;
313                  interval = interval.parent);
314
315           return interval;
316         }
317       }
318
319       private MInterval Right {
320         get {
321           MInterval interval;
322
323           if (right != null)
324             for (interval = right;
325                  interval.left != null;
326                  interval = interval.left);
327           else
328             for (interval = parent;
329                  interval != null && interval.total_start < 0;
330                  interval = interval.parent);
331
332           return interval;
333         }
334       }
335
336       public void Push (int start, int end, MTextProperty prop)
337       {
338         start <<= 2;
339         if (prop.FrontSticky)
340           start--;
341         else
342           start++;
343         end <<= 2;
344         if (prop.RearSticky)
345           end++;
346         else
347           end--;
348         if (start >= end)
349           throw new Exception ("Invalid Text Property Range");
350
351         push (start, end, prop);
352       }
353
354       private MInterval divide_right (int pos)
355       {
356         MInterval interval = Copy ();
357         int this_start = Start;
358         int this_end = End;
359
360         if (left == null || (right != null && left.depth < right.depth))
361           {
362             interval.left = this;
363             interval.right = right;
364             interval.parent = parent;
365             if (parent != null)
366               {
367                 if (total_start == 0)
368                   parent.left = interval;
369                 else
370                   parent.right = interval;
371               }
372             interval.depth = depth;
373             if (right == null)
374               interval.depth++;
375             total_start = 0;
376             total_end = pos - this_start;
377           }
378         else
379           {
380             interval.total_start = pos - this_end;
381             interval.total_end = 0;
382             if (right != null)
383               right.parent = interval;
384             right = interval;
385           }
386
387         return interval;
388       }
389
390       private MInterval divide_left (int pos)
391       {
392         MInterval interval = Copy ();
393         int this_start = Start;
394         int this_end = End;
395
396         if (left == null || (right != null && left.depth < right.depth))
397           {
398             interval.total_start = 0;
399             interval.total_end = pos - this_start;
400             if (left != null)
401               left.parent = interval;
402             left = interval;
403           }
404         else
405           {
406             interval.right = this;
407             interval.left = left;
408             interval.parent = parent;
409             if (parent != null)
410               {
411                 if (total_start == 0)
412                   parent.left = interval;
413                 else
414                   parent.right = interval;
415               }
416             interval.depth = depth;
417             if (left == null)
418               interval.depth++;
419             total_start = pos - this_end;
420             total_end = 0;
421           }
422
423         return interval;
424       }
425
426       private void push (int start, int end, MTextProperty prop)
427       {
428         int this_start = Start;
429         int this_end = End;
430
431         if (start < this_start)
432           {
433             if (end <= this_start)
434               {
435                 Left.push (start, end, prop);
436                 return;
437               }
438             Left.push (start, this_start, prop);
439             start = this_start;
440           }
441         if (this_end < end)
442           {
443             if (this_end < start)
444               {
445                 Right.push (start, end, prop);
446                 return;
447               }
448             Right.push (this_end, end, prop);
449             end = this_end;
450           }
451         if (this_start < start)
452           divide_left (start);
453         if (end < this_end)
454           divide_right (end);
455         stack.Push (prop);
456       }
457     }
458   }
459 }