*** empty log message ***
[m17n/m17n-lib-cs.git] / MCharTable.cs
index 6845767..845b6ee 100644 (file)
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.IO;
 using M17N;
 using M17N.Core;
 
 namespace M17N.Core
 {
-  public class MCharTable : IEnumerable
+  public class MCharRange
+  {
+    internal int from;
+    internal int to;
+    internal object value;
+    internal MCharTable from_table;
+    internal MCharTable to_table;
+
+    public int From { get { return from; } }
+    public int To { get { return to; } }
+    public object Value { get { return value; } }
+
+    internal static void CheckChar (int c)
+    {
+      if (c < 0 || c > 0x10FFFF)
+       throw new ArgumentException ("Invalid Unicode character: " + c);
+    }
+    
+    public MCharRange (int c, MCharTable table)
+    {
+      CheckChar (c);
+      value = table.Get (c, out table);
+      if (c == 0)
+       {
+         from = c;
+         from_table = table;
+       }
+      else
+       from = table.PrevBoundary (c - 1, value, out from_table) + 1;
+      if (c == 0x10FFFF)
+       {
+         to = c;
+         to_table = table;
+       }
+      else
+       to = table.NextBoundary (c + 1, value, out to_table) - 1;
+    }
+
+    public bool Prev ()
+    {
+      if (from == 0)
+       return false;
+      to = from - 1;
+      value = from_table.Get (to, out to_table);
+      if (to == 0)
+       {
+         from = to;
+         from_table = to_table;
+       }
+      else
+       from = to_table.PrevBoundary (to - 1, value, out from_table) + 1;
+      return true;
+    }
+
+    public bool Next ()
+    {
+      if (to == 0x10FFFF)
+       return false;
+      from = to + 1;
+      value = to_table.Get (from, out from_table);
+      if (from == 0x10FFFF)
+       {
+         to = from;
+         to_table = from_table;
+       }
+      else
+       to = from_table.NextBoundary (from + 1, value, out to_table) - 1;
+      return true;
+    }
+
+    public override string ToString ()
+    {
+      return ((from == to)
+             ? String.Format ("[U+{0:X} {1}]", from,
+                              value == null ? "null" : value)
+             : String.Format ("[U+{0:X}..U+{1:X} {2}]", from, to,
+                              value == null ? "null" : value));
+    }
+  }
+
+  public class MCharTable : IEnumerable<MCharRange>
   {
     private static readonly int[] nchars
       = new int[] { 0x110000, 0x10000, 0x1000, 0x80 };
     private static readonly int[] slots
       = new int[] { 17, 16, 32, 128 };
-    private static readonly int[] mask
-      = new int[] { 0x11FFFF, 0xFFFF, 0xFFF, 0x7F };
     private static readonly int[] shift
       = new int[] { 16, 12, 7, 0 };
 
-    private int index (int c) { return ((c & mask[depth]) >> shift[depth]); }
+    private static bool IsSubCharTable (object obj)
+      {
+       return (obj is MCharTable
+               && ((MCharTable) obj).depth > 0);
+      }
+
+    private int index (int c) { return ((c - min_char) >> shift[depth]); }
 
     private MCharTable parent;
     private int depth;
@@ -28,8 +113,8 @@ namespace M17N.Core
       {
        parent = null;
        depth = 0;
-       min_char = 0x110000;
-       max_char = 0;
+       min_char = 0;
+       max_char = 0x10FFFF;
        contents = new object[slots[0]];
       }
 
@@ -47,22 +132,32 @@ namespace M17N.Core
     private object Get (int c)
     {
       object slot = contents[index (c)];
-      if (slot is MCharTable)
+      if (IsSubCharTable (slot))
        return ((MCharTable) slot).Get (c);
       return slot;
     }
 
+    internal object Get (int c, out MCharTable table)
+    {
+      if (c < min_char || c > max_char)
+       return parent.Get (c, out table);
+      object slot = contents[index (c)];
+      if (IsSubCharTable (slot))
+       return ((MCharTable) slot).Get (c, out table);
+      table = this;
+      return slot;
+    }
+
     private void Set (int c, object value)
     {
+      int i = index (c);
+
       if (depth == 3)
-       contents[c & mask[3]] = value;
+       contents[i] = value;
       else
        {
-         int i = index (c);
-         int offset = depth == 0 ? 0 : min_char;
-
-         if (! (contents[i] is MCharTable))
-           contents[i] = new MCharTable (this, offset + i << shift[depth],
+         if (! IsSubCharTable (contents[i]))
+           contents[i] = new MCharTable (this, min_char + (i << shift[depth]),
                                          contents[i]);
          ((MCharTable) contents[i]).Set (c, value);
        }
@@ -71,98 +166,241 @@ namespace M17N.Core
     public object this[int c]
     {
       get {
-       if (c < 0 || c > 0x10FFFF)
-         throw new ArgumentException ("Invalid character: " + c);
-       if (min_char > c || max_char < c)
-         return null;
+       MCharRange.CheckChar (c);
        return Get (c);
       }
 
       set {
-       if (c < 0 || c > 0x10FFFF)
-         throw new ArgumentException ("Invalid character: " + c);
-       if (value is MCharTable)
-         throw new ArgumentException ("Invalid value type: MCharTable");
-       if (min_char > c)
-         min_char = c;
-       if (max_char < c)
-         max_char = c;
+       MCharRange.CheckChar (c);
        Set (c, value);
       }
     }
 
-    private class MCharTableIT
+    public object this[int from, int to]
     {
-      public int c;
-      public object value;
-      public MCharTable table;
+      set {
+       MCharRange.CheckChar (from);
 
-      public MCharTableIT (int c, object value, MCharTable table)
-      {
-       this.c = c;
-       this.value = value;
-       this.table = table;
+       if (from == to)
+         Set (from, value);
+       else
+         {
+           MCharRange.CheckChar (to);
+           set_range (from, to, value);
+         }
       }
+    }
 
-      public override string ToString ()
-      {
-       return (String.Format ("<{0:X},{1}>", c, value));
-      }
+    private void set_range (int from, int to, object value)
+    {
+      if (to > max_char)
+       to = max_char;
+      int i0 = index (from), i1 = index (to);
+
+      if (depth == 3)
+       {
+         for (; i0 <= i1; i0++)
+           contents[i0] = value;
+       }
+      else
+       {
+         int min0 = min_char + (i0 << shift[depth]);
+         int min1 = min_char + (i1 << shift[depth]);
+
+         if (min0 < from)
+           {
+             if (contents[i0] != value)
+               {
+                 if (! IsSubCharTable (contents[i0]))
+                   contents[i0] = new MCharTable (this, min0, contents[i0]);
+                 ((MCharTable) contents[i0]).set_range (from, to, value);
+               }
+             from = min0 + nchars[depth + 1];
+             if (from >= to)
+               return;
+             i0++;
+           }
+         for (; i0 < i1; i0++)
+           contents[i0] = value;
+         if (to == min1 + nchars[depth + 1] - 1)
+           contents[i1] = value;
+         else if (contents[i1] != value)
+           {
+             if (! IsSubCharTable (contents[i1]))
+               contents[i1] = new MCharTable (this, min1, contents[i1]);
+             ((MCharTable) contents[i1]).set_range (min1, to, value);
+           }
+       }
     }
 
-    private bool Next (MCharTableIT it)
+    internal int NextBoundary (int c, object value, out MCharTable table)
     {
-      Console.WriteLine ("Depth:{0} ({1:X}-{2:X}) IT:{3}", depth, min_char, max_char, it);
-      if (it.c > max_char)
-       return (parent != null ? parent.Next (it) : false);
+      table = this;
+      for (int i = index (c); i < slots[depth];
+          i++, c = min_char + (i << shift[depth]))
+       if (contents[i] != value)
+         return (IsSubCharTable (contents[i])
+                 ? ((MCharTable) contents[i]).NextBoundary (c, value,
+                                                            out table)
+                 : c);
+      return (depth == 0
+             ? c : parent.NextBoundary (c, value, out table));
+    }
 
-      object value = null;
-      for (int i = index (it.c);
-          i < slots[depth] && (value = contents[i]) == null;
-          i++, it.c = min_char + i << shift[depth]);
-      Console.WriteLine ("IT:{1}", depth, it);
-      if (value == null)
-       return (parent != null ? parent.Next (it) : false);
-      if (value is MCharTable)
-       return ((MCharTable) value).Next (it);
-      it.table = this;
-      it.value = value;
-      return true;
+    internal int PrevBoundary (int c, object value, out MCharTable table)
+    {
+      table = this;
+      for (int i = index (c); i >= 0;
+          c = min_char + (i << shift[depth]) - 1, i--)
+       if (contents[i] != value)
+         return (IsSubCharTable (contents[i])
+                 ? ((MCharTable) contents[i]).PrevBoundary (c, value,
+                                                            out table)
+                 : c);
+      return (depth == 0
+             ? c : parent.PrevBoundary (c, value, out table));
     }
 
     // for IEnumerable interface
-    public IEnumerator GetEnumerator()
+    public IEnumerator<MCharRange> GetEnumerator()
     {
       return new MCharTableEnum (this);
     }
 
-    private class MCharTableEnum : IEnumerator
+    IEnumerator IEnumerable.GetEnumerator()
     {
-      private MCharTable table;
-      private MCharTableIT it;
+      return GetEnumerator ();
+    }
 
-      public MCharTableEnum (MCharTable table)
-       {
-         this.table = table;
-         it = new MCharTableIT (-1, null, table);
-       }
+    private class MCharTableEnum : IEnumerator<MCharRange>
+    {
+      MCharTable table;
+      private MCharRange range;
+
+      public MCharTableEnum (MCharTable table) {
+       this.table = table;
+      }
 
       public bool MoveNext ()
       {
-       it.c++;
-       return it.table.Next (it);
+       if (range == null)
+         {
+           range = new MCharRange (0, table);
+           return true;
+         }
+       return range.Next ();
       }
 
-      public void Reset ()
+      public void Reset () { range = null; }
+
+      public MCharRange Current { get { return range; } }
+
+      object IEnumerator.Current { get { return Current; } }
+
+      public void Dispose () {}
+    }
+  }
+
+  public class MCharProp : MCharTable
+  {
+    private static Dictionary<MSymbol, MDatabase> char_prop
+      = new Dictionary<MSymbol, MDatabase> ();
+
+    public static void Define (MSymbol prop, MDatabase mdb)
       {
-       it.c = -1;
-       it.table = table;
+       char_prop[prop] = mdb;
       }
 
-      public KeyValuePair<int, object> Current
+    public MCharProp (MSymbol prop)
       {
-        get { return new KeyValuePair<int, object> (it.c, it.value); }
+       MDatabase mdb;
+
+       if (! char_prop.TryGetValue (prop, out mdb))
+         throw new Exception ("Undefined character property: " + prop);
+       mdb.Load (this);
       }
+  }
+
+  public partial class MDatabase : IComparable<MDatabase>
+  {
+    private bool read_range (MStreamReader mst, out int from, out int to)
+    {
+      if (! mst.ReadInteger (out from))
+       {
+         to = from;
+         return false;
+       }
+      to = mst.Read ();
+      if (to < 0)
+       return false;
+      if (to != '-')
+       {
+         to = from;
+         return true;
+       }
+      return mst.ReadInteger (out to);
+    }
+
+    private void load_char_table (MCharTable table)
+    {
+      MSymbol type = tag[1];
+      
+      using (FileStream stream = FileInfo.OpenRead ())
+       {
+         MStreamReader mst = new MStreamReader (stream, ';', true);
+         int c, from, to;
+
+         while ((c = mst.Peek ()) >= 0)
+           {
+             if (c != '#'
+                 && read_range (mst, out from, out to)
+                 && mst.SkipSpace (out c))
+               {
+                 object value = null;
+
+                 if (type == MSymbol.integer)
+                   {
+                     int i;
+                     if (mst.ReadInteger (out i))
+                       value = i;
+                   }
+                 else if (type == MSymbol.symbol)
+                   {
+                     MSymbol sym;
+                     if (mst.ReadSymbol (out sym, -1))
+                       value = sym;
+                   }
+                 else if (type == MSymbol.mtext)
+                   {
+                     MText mt;
+                     if (mst.ReadMText (out mt))
+                       value = mt;
+                   }
+                 else if (type == MSymbol.plist)
+                   {
+                     value = new MPlist (mst);
+                   }
+                 else if (type == MSymbol.mstring)
+                   {
+                     string str;
+                     if (mst.ReadString (out str))
+                       value = str;
+                   }
+                 if (value != null)
+                   table[from, to] = value;
+               }
+             mst.ForwardLine ();
+           }
+       }
+    }
+
+    public void Load (MCharTable table)
+    {
+      if (loader != null || Info.Format != Mchar_table)
+       throw new ArgumentException ("Not a database of CharTable type");
+      if (! update_status ())
+       throw new Exception ("Database invalid");
+      load_char_table (table);
     }
   }
 }