*** empty log message ***
[m17n/m17n-lib-cs.git] / MCharTable.cs
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.IO;
5 using M17N;
6 using M17N.Core;
7
8 namespace M17N.Core
9 {
10   public class MCharRange
11   {
12     internal int from;
13     internal int to;
14     internal object value;
15     internal MCharTable from_table;
16     internal MCharTable to_table;
17
18     public int From { get { return from; } }
19     public int To { get { return to; } }
20     public object Value { get { return value; } }
21
22     internal static void CheckChar (int c)
23     {
24       if (c < 0 || c > 0x10FFFF)
25         throw new ArgumentException ("Invalid Unicode character: " + c);
26     }
27     
28     public MCharRange (int c, MCharTable table)
29     {
30       CheckChar (c);
31       value = table.Get (c, out table);
32       if (c == 0)
33         {
34           from = c;
35           from_table = table;
36         }
37       else
38         from = table.PrevBoundary (c - 1, value, out from_table) + 1;
39       if (c == 0x10FFFF)
40         {
41           to = c;
42           to_table = table;
43         }
44       else
45         to = table.NextBoundary (c + 1, value, out to_table) - 1;
46     }
47
48     public bool Prev ()
49     {
50       if (from == 0)
51         return false;
52       to = from - 1;
53       value = from_table.Get (to, out to_table);
54       if (to == 0)
55         {
56           from = to;
57           from_table = to_table;
58         }
59       else
60         from = to_table.PrevBoundary (to - 1, value, out from_table) + 1;
61       return true;
62     }
63
64     public bool Next ()
65     {
66       if (to == 0x10FFFF)
67         return false;
68       from = to + 1;
69       value = to_table.Get (from, out from_table);
70       if (from == 0x10FFFF)
71         {
72           to = from;
73           to_table = from_table;
74         }
75       else
76         to = from_table.NextBoundary (from + 1, value, out to_table) - 1;
77       return true;
78     }
79
80     public override string ToString ()
81     {
82       return ((from == to)
83               ? String.Format ("[U+{0:X} {1}]", from,
84                                value == null ? "null" : value)
85               : String.Format ("[U+{0:X}..U+{1:X} {2}]", from, to,
86                                value == null ? "null" : value));
87     }
88   }
89
90   public class MCharTable : IEnumerable<MCharRange>
91   {
92     private static readonly int[] nchars
93       = new int[] { 0x110000, 0x10000, 0x1000, 0x80 };
94     private static readonly int[] slots
95       = new int[] { 17, 16, 32, 128 };
96     private static readonly int[] shift
97       = new int[] { 16, 12, 7, 0 };
98
99     private static bool IsSubCharTable (object obj)
100       {
101         return (obj is MCharTable
102                 && ((MCharTable) obj).depth > 0);
103       }
104
105     private int index (int c) { return ((c - min_char) >> shift[depth]); }
106
107     private MCharTable parent;
108     private int depth;
109     private int min_char, max_char;
110     private object[] contents;
111
112     public MCharTable ()
113       {
114         parent = null;
115         depth = 0;
116         min_char = 0;
117         max_char = 0x10FFFF;
118         contents = new object[slots[0]];
119       }
120
121     private MCharTable (MCharTable parent, int min_char, object value)
122       {
123         this.parent = parent;
124         this.min_char = min_char;
125         depth = parent.depth + 1;
126         max_char = min_char + nchars[depth] - 1;
127         contents = new object[slots[depth]];
128         for (int i = 0; i < slots[depth]; i++)
129           contents[i] = value;
130       }
131
132     private object Get (int c)
133     {
134       object slot = contents[index (c)];
135       if (IsSubCharTable (slot))
136         return ((MCharTable) slot).Get (c);
137       return slot;
138     }
139
140     internal object Get (int c, out MCharTable table)
141     {
142       if (c < min_char || c > max_char)
143         return parent.Get (c, out table);
144       object slot = contents[index (c)];
145       if (IsSubCharTable (slot))
146         return ((MCharTable) slot).Get (c, out table);
147       table = this;
148       return slot;
149     }
150
151     private void Set (int c, object value)
152     {
153       int i = index (c);
154
155       if (depth == 3)
156         contents[i] = value;
157       else
158         {
159           if (! IsSubCharTable (contents[i]))
160             contents[i] = new MCharTable (this, min_char + (i << shift[depth]),
161                                           contents[i]);
162           ((MCharTable) contents[i]).Set (c, value);
163         }
164     }
165
166     public object this[int c]
167     {
168       get {
169         MCharRange.CheckChar (c);
170         return Get (c);
171       }
172
173       set {
174         MCharRange.CheckChar (c);
175         Set (c, value);
176       }
177     }
178
179     public object this[int from, int to]
180     {
181       set {
182         MCharRange.CheckChar (from);
183
184         if (from == to)
185           Set (from, value);
186         else
187           {
188             MCharRange.CheckChar (to);
189             set_range (from, to, value);
190           }
191       }
192     }
193
194     private void set_range (int from, int to, object value)
195     {
196       if (to > max_char)
197         to = max_char;
198       int i0 = index (from), i1 = index (to);
199
200       if (depth == 3)
201         {
202           for (; i0 <= i1; i0++)
203             contents[i0] = value;
204         }
205       else
206         {
207           int min0 = min_char + (i0 << shift[depth]);
208           int min1 = min_char + (i1 << shift[depth]);
209
210           if (min0 < from)
211             {
212               if (contents[i0] != value)
213                 {
214                   if (! IsSubCharTable (contents[i0]))
215                     contents[i0] = new MCharTable (this, min0, contents[i0]);
216                   ((MCharTable) contents[i0]).set_range (from, to, value);
217                 }
218               from = min0 + nchars[depth + 1];
219               if (from >= to)
220                 return;
221               i0++;
222             }
223           for (; i0 < i1; i0++)
224             contents[i0] = value;
225           if (to == min1 + nchars[depth + 1] - 1)
226             contents[i1] = value;
227           else if (contents[i1] != value)
228             {
229               if (! IsSubCharTable (contents[i1]))
230                 contents[i1] = new MCharTable (this, min1, contents[i1]);
231               ((MCharTable) contents[i1]).set_range (min1, to, value);
232             }
233         }
234     }
235
236     internal int NextBoundary (int c, object value, out MCharTable table)
237     {
238       table = this;
239       for (int i = index (c); i < slots[depth];
240            i++, c = min_char + (i << shift[depth]))
241         if (contents[i] != value)
242           return (IsSubCharTable (contents[i])
243                   ? ((MCharTable) contents[i]).NextBoundary (c, value,
244                                                              out table)
245                   : c);
246       return (depth == 0
247               ? c : parent.NextBoundary (c, value, out table));
248     }
249
250     internal int PrevBoundary (int c, object value, out MCharTable table)
251     {
252       table = this;
253       for (int i = index (c); i >= 0;
254            c = min_char + (i << shift[depth]) - 1, i--)
255         if (contents[i] != value)
256           return (IsSubCharTable (contents[i])
257                   ? ((MCharTable) contents[i]).PrevBoundary (c, value,
258                                                              out table)
259                   : c);
260       return (depth == 0
261               ? c : parent.PrevBoundary (c, value, out table));
262     }
263
264     // for IEnumerable interface
265     public IEnumerator<MCharRange> GetEnumerator()
266     {
267       return new MCharTableEnum (this);
268     }
269
270     IEnumerator IEnumerable.GetEnumerator()
271     {
272       return GetEnumerator ();
273     }
274
275     private class MCharTableEnum : IEnumerator<MCharRange>
276     {
277       MCharTable table;
278       private MCharRange range;
279
280       public MCharTableEnum (MCharTable table) {
281         this.table = table;
282       }
283
284       public bool MoveNext ()
285       {
286         if (range == null)
287           {
288             range = new MCharRange (0, table);
289             return true;
290           }
291         return range.Next ();
292       }
293
294       public void Reset () { range = null; }
295
296       public MCharRange Current { get { return range; } }
297
298       object IEnumerator.Current { get { return Current; } }
299
300       public void Dispose () {}
301     }
302   }
303
304   public class MCharProp : MCharTable
305   {
306     private static Dictionary<MSymbol, MDatabase> char_prop
307       = new Dictionary<MSymbol, MDatabase> ();
308
309     public static void Define (MSymbol prop, MDatabase mdb)
310       {
311         char_prop[prop] = mdb;
312       }
313
314     public MCharProp (MSymbol prop)
315       {
316         MDatabase mdb;
317
318         if (! char_prop.TryGetValue (prop, out mdb))
319           throw new Exception ("Undefined character property: " + prop);
320         mdb.Load (this);
321       }
322   }
323
324   public partial class MDatabase : IComparable<MDatabase>
325   {
326     private bool read_range (MStreamReader mst, out int from, out int to)
327     {
328       if (! mst.ReadInteger (out from))
329         {
330           to = from;
331           return false;
332         }
333       to = mst.Read ();
334       if (to < 0)
335         return false;
336       if (to != '-')
337         {
338           to = from;
339           return true;
340         }
341       return mst.ReadInteger (out to);
342     }
343
344     private MCharTable load_char_table (MCharTable table)
345     {
346       MSymbol type = tag[1];
347       
348       using (FileStream stream = FileInfo.OpenRead ())
349         {
350           MStreamReader mst = new MStreamReader (stream, ';', true);
351           int c, from, to;
352
353           while ((c = mst.Peek ()) >= 0)
354             {
355               if (c != '#'
356                   && read_range (mst, out from, out to)
357                   && mst.SkipSpace (out c))
358                 {
359                   object value = null;
360
361                   if (type == MSymbol.integer)
362                     {
363                       int i;
364                       if (mst.ReadInteger (out i))
365                         value = i;
366                     }
367                   else if (type == MSymbol.symbol)
368                     {
369                       MSymbol sym;
370                       if (mst.ReadSymbol (out sym, -1))
371                         value = sym;
372                     }
373                   else if (type == MSymbol.mtext)
374                     {
375                       MText mt;
376                       if (mst.ReadMText (out mt))
377                         value = mt;
378                     }
379                   else if (type == MSymbol.plist)
380                     {
381                       value = new MPlist (mst);
382                     }
383                   else if (type == MSymbol.mstring)
384                     {
385                       string str;
386                       if (mst.ReadString (out str))
387                         value = str;
388                     }
389                   if (value != null)
390                     table[from, to] = value;
391                 }
392               mst.ForwardLine ();
393             }
394         }
395       return table;
396     }
397
398     public object Load (MCharTable table)
399     {
400       if (loader != null || Info.Format != Mchar_table)
401         throw new ArgumentException ("Not a database of CharTable type");
402       if (! update_status ())
403         throw new Exception ("Database invalid");
404       return load_char_table (table);
405     }
406   }
407 }