*** empty log message ***
[m17n/m17n-lib-cs.git] / MCharTable.cs
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using M17N;
5 using M17N.Core;
6
7 namespace M17N.Core
8 {
9   public class MCharRange
10   {
11     internal int from;
12     internal int to;
13     internal object value;
14     internal MCharTable from_table;
15     internal MCharTable to_table;
16
17     public int From { get { return from; } }
18     public int To { get { return to; } }
19     public object Value { get { return value; } }
20
21     internal static void CheckChar (int c)
22     {
23       if (c < 0 || c > 0x10FFFF)
24         throw new ArgumentException ("Invalid Unicode character: " + c);
25     }
26     
27     public MCharRange (int c, MCharTable table)
28     {
29       CheckChar (c);
30       value = table.Get (c, out table);
31       if (c == 0)
32         {
33           from = c;
34           from_table = table;
35         }
36       else
37         from = table.PrevBoundary (c - 1, value, out from_table) + 1;
38       if (c == 0x10FFFF)
39         {
40           to = c;
41           to_table = table;
42         }
43       else
44         to = table.NextBoundary (c + 1, value, out to_table) - 1;
45     }
46
47     public bool Prev ()
48     {
49       if (from == 0)
50         return false;
51       to = from - 1;
52       value = from_table.Get (to, out to_table);
53       if (to == 0)
54         {
55           from = to;
56           from_table = to_table;
57         }
58       else
59         from = to_table.PrevBoundary (to - 1, value, out from_table) + 1;
60       return true;
61     }
62
63     public bool Next ()
64     {
65       if (to == 0x10FFFF)
66         return false;
67       from = to + 1;
68       value = to_table.Get (from, out from_table);
69       if (from == 0x10FFFF)
70         {
71           to = from;
72           to_table = from_table;
73         }
74       else
75         to = from_table.NextBoundary (from + 1, value, out to_table) - 1;
76       return true;
77     }
78
79     public override string ToString ()
80     {
81       return String.Format ("[U+{0:X}..U+{1:X} {2}]", from, to,
82                             value == null ? "null" : value);
83     }
84   }
85
86   public class MCharTable : IEnumerable<MCharRange>
87   {
88     private static readonly int[] nchars
89       = new int[] { 0x110000, 0x10000, 0x1000, 0x80 };
90     private static readonly int[] slots
91       = new int[] { 17, 16, 32, 128 };
92     private static readonly int[] shift
93       = new int[] { 16, 12, 7, 0 };
94
95     private static bool IsSubCharTable (object obj)
96       {
97         return (obj is MCharTable
98                 && ((MCharTable) obj).depth > 0);
99       }
100
101     private int index (int c) { return ((c - min_char) >> shift[depth]); }
102
103     private MCharTable parent;
104     private int depth;
105     private int min_char, max_char;
106     private object[] contents;
107
108     public MCharTable ()
109       {
110         parent = null;
111         depth = 0;
112         min_char = 0;
113         max_char = 0x10FFFF;
114         contents = new object[slots[0]];
115       }
116
117     private MCharTable (MCharTable parent, int min_char, object value)
118       {
119         this.parent = parent;
120         this.min_char = min_char;
121         depth = parent.depth + 1;
122         max_char = min_char + nchars[depth] - 1;
123         contents = new object[slots[depth]];
124         for (int i = 0; i < slots[depth]; i++)
125           contents[i] = value;
126       }
127
128     private object Get (int c)
129     {
130       object slot = contents[index (c)];
131       if (IsSubCharTable (slot))
132         return ((MCharTable) slot).Get (c);
133       return slot;
134     }
135
136     internal object Get (int c, out MCharTable table)
137     {
138       if (c < min_char || c > max_char)
139         return parent.Get (c, out table);
140       object slot = contents[index (c)];
141       if (IsSubCharTable (slot))
142         return ((MCharTable) slot).Get (c, out table);
143       table = this;
144       return slot;
145     }
146
147     private void Set (int c, object value)
148     {
149       int i = index (c);
150
151       if (depth == 3)
152         contents[i] = value;
153       else
154         {
155           if (! IsSubCharTable (contents[i]))
156             contents[i] = new MCharTable (this, min_char + (i << shift[depth]),
157                                           contents[i]);
158           ((MCharTable) contents[i]).Set (c, value);
159         }
160     }
161
162     public object this[int c]
163     {
164       get {
165         MCharRange.CheckChar (c);
166         return Get (c);
167       }
168
169       set {
170         MCharRange.CheckChar (c);
171         Set (c, value);
172       }
173     }
174
175     public object this[int from, int to]
176     {
177       set { set_range (from, to, value); }
178     }
179
180     private void set_range (int from, int to, object value)
181     {
182       if (to > max_char)
183         to = max_char;
184       int i0 = index (from), i1 = index (to);
185
186       if (depth == 3)
187         {
188           for (; i0 <= i1; i0++)
189             contents[i0] = value;
190         }
191       else
192         {
193           int min0 = min_char + (i0 << shift[depth]);
194           int min1 = min_char + (i1 << shift[depth]);
195
196           if (min0 < from)
197             {
198               if (contents[i0] != value)
199                 {
200                   if (! IsSubCharTable (contents[i0]))
201                     contents[i0] = new MCharTable (this, min0, contents[i0]);
202                   ((MCharTable) contents[i0]).set_range (from, to, value);
203                 }
204               from = min0 + nchars[depth + 1];
205               if (from >= to)
206                 return;
207               i0++;
208             }
209           for (; i0 < i1; i0++)
210             contents[i0] = value;
211           if (to == min1 + nchars[depth + 1] - 1)
212             contents[i1] = value;
213           else if (contents[i1] != value)
214             {
215               if (! IsSubCharTable (contents[i1]))
216                 contents[i1] = new MCharTable (this, min1, contents[i1]);
217               ((MCharTable) contents[i1]).set_range (min1, to, value);
218             }
219         }
220     }
221
222     internal int NextBoundary (int c, object value, out MCharTable table)
223     {
224       table = this;
225       for (int i = index (c); i < slots[depth];
226            i++, c = min_char + (i << shift[depth]))
227         if (contents[i] != value)
228           return (IsSubCharTable (contents[i])
229                   ? ((MCharTable) contents[i]).NextBoundary (c, value,
230                                                              out table)
231                   : c);
232       return (depth == 0
233               ? c : parent.NextBoundary (c, value, out table));
234     }
235
236     internal int PrevBoundary (int c, object value, out MCharTable table)
237     {
238       table = this;
239       for (int i = index (c); i >= 0;
240            c = min_char + (i << shift[depth]) - 1, i--)
241         if (contents[i] != value)
242           return (IsSubCharTable (contents[i])
243                   ? ((MCharTable) contents[i]).PrevBoundary (c, value,
244                                                              out table)
245                   : c);
246       return (depth == 0
247               ? c : parent.PrevBoundary (c, value, out table));
248     }
249
250     // for IEnumerable interface
251     public IEnumerator<MCharRange> GetEnumerator()
252     {
253       return new MCharTableEnum (this);
254     }
255
256     IEnumerator IEnumerable.GetEnumerator()
257     {
258       return GetEnumerator ();
259     }
260
261     private class MCharTableEnum : IEnumerator<MCharRange>
262     {
263       MCharTable table;
264       private MCharRange range;
265
266       public MCharTableEnum (MCharTable table)
267         {
268           this.table = table;
269         }
270
271       public void Dispose () {}
272
273       public bool MoveNext ()
274       {
275         if (range == null)
276           {
277             range = new MCharRange (0, table);
278             return true;
279           }
280         return range.Next ();
281       }
282
283       public void Reset ()
284       {
285         range = null;
286       }
287
288       public MCharRange Current
289       {
290         get { return range; }
291       }
292
293       object IEnumerator.Current
294       {
295         get { return Current; }
296       }
297     }
298   }
299 }