using System; using System.Collections; using System.Collections.Generic; using System.IO; using M17N; using M17N.Core; namespace M17N.Core { 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 { 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[] shift = new int[] { 16, 12, 7, 0 }; 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; private int min_char, max_char; private object[] contents; public MCharTable () { parent = null; depth = 0; min_char = 0; max_char = 0x10FFFF; contents = new object[slots[0]]; } private MCharTable (MCharTable parent, int min_char, object value) { this.parent = parent; this.min_char = min_char; depth = parent.depth + 1; max_char = min_char + nchars[depth] - 1; contents = new object[slots[depth]]; for (int i = 0; i < slots[depth]; i++) contents[i] = value; } private object Get (int c) { object slot = contents[index (c)]; 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[i] = value; else { if (! IsSubCharTable (contents[i])) contents[i] = new MCharTable (this, min_char + (i << shift[depth]), contents[i]); ((MCharTable) contents[i]).Set (c, value); } } public object this[int c] { get { MCharRange.CheckChar (c); return Get (c); } set { MCharRange.CheckChar (c); Set (c, value); } } public object this[int from, int to] { set { MCharRange.CheckChar (from); if (from == to) Set (from, value); else { MCharRange.CheckChar (to); set_range (from, to, 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); } } } internal int NextBoundary (int c, object value, out MCharTable table) { 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)); } 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() { return new MCharTableEnum (this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator (); } private class MCharTableEnum : IEnumerator { MCharTable table; private MCharRange range; public MCharTableEnum (MCharTable table) { this.table = table; } public bool MoveNext () { if (range == null) { range = new MCharRange (0, table); return true; } return range.Next (); } 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 char_prop = new Dictionary (); public static void Define (MSymbol prop, MDatabase mdb) { char_prop[prop] = mdb; } public MCharProp (MSymbol prop) { MDatabase mdb; if (! char_prop.TryGetValue (prop, out mdb)) throw new Exception ("Undefined character property: " + prop); mdb.Load (this); } } public partial class MDatabase : IComparable { 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); } } }