using System; using System.Collections.Generic; using System.IO; using M17N; using M17N.Core; namespace M17N.Core { public delegate object MDatabaseLoader (MDatabaseTag tag, object extra_info); public struct MDatabaseTag { public MSymbol Tag0, Tag1, Tag2, Tag3; public MDatabaseTag (MSymbol tag0) { Tag0 = tag0; Tag1 = Tag2 = Tag3 = MSymbol.nil; } public MDatabaseTag (MSymbol tag0, MSymbol tag1) { Tag0 = tag0; Tag1 = tag1; Tag2 = Tag3 = MSymbol.nil; } public MDatabaseTag (MSymbol tag0, MSymbol tag1, MSymbol tag2) { Tag0 = tag0; Tag1 = tag1; Tag2 = tag2; Tag3 = MSymbol.nil; } public MDatabaseTag (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3) { Tag0 = tag0; Tag1 = tag1; Tag2 = tag2; Tag3 = tag3; } } public class MDatabase { private class MDatabaseDir { private const string ListFileName = "mdb.dir"; public string Dirname; public DirectoryInfo DirInfo; public DateTime LastScanned; public FileInfo ListInfo; private static void GetInfo (string dirname, out DirectoryInfo dirinfo, out FileInfo listinfo) { if (Directory.Exists (dirname)) try { dirinfo = new DirectoryInfo (dirname); try { listinfo = dirinfo.GetFiles (ListFileName)[0]; } catch { listinfo = null; } } catch { dirinfo = null; listinfo = null; } else { dirinfo = null; listinfo = null; } } public MDatabaseDir (string dirname) { Dirname = dirname; GetInfo (dirname, out DirInfo, out ListInfo); } public bool StatusChanged { get { bool exists = Directory.Exists (Dirname); if (DirInfo != null) { if (! exists) { DirInfo = null; ListInfo = null; LastScanned = new DateTime (0); } if (LastScanned.Year == 0) return true; DirInfo.Refresh (); if (ListInfo != null) ListInfo.Refresh (); return (LastScanned < DirInfo.LastWriteTime || LastScanned < ListInfo.LastWriteTime); } else { if (exists) { DirInfo = new DirectoryInfo (Dirname); try { ListInfo = DirInfo.GetFiles (ListFileName)[0]; } catch { ListInfo = null; } return true; } return false; } } } public void UpdateStatus () { if (DirInfo != null) LastScanned = DateTime.UtcNow; } public FileInfo[] Scan (string filename) { if (DirInfo == null) return null; DirInfo.Refresh (); return DirInfo.GetFiles (filename); } } internal class MDatabaseInfo { internal DirectoryInfo Dir; internal string Description; internal MText Filename; internal FileInfo FileInfo; internal FileInfo Validater; internal int Version; internal MSymbol Format; internal MSymbol Schema; internal MText SchemaFile; internal DateTime ModifiedTime; internal MPlist Props; } private static Dictionary DBDict = new Dictionary (); private static MDatabaseDir[] DBDirs = new MDatabaseDir[3]; private const string SystemDirectory = "/usr/share/m17n"; private readonly MSymbol Mversion = MSymbol.Of ("version"); /// Type of database private enum MDBType { /// The database was defined automatically from mdb.dir /// file(s) with no wildcard tag. AUTO, /// The database was defined automatically from mdb.dir /// file(s) with a wildcard tag to define multiple databases /// of the same kind. MULTIPLE, /// The database was defined explicitely by MDatabase.Define /// without a special loader. EXPLICIT, /// The database was defined explicitely by MDatabase.Define /// with a special loader. UNKNOWN, }; /// Status of database private enum MDBStatus { // The database file is currently disabled. It means that the // database file is not readable or the database is deleted by // the modification of "mdb.dir". DISABLED, // The database file has not yet been loaded, or was modified // after the previous loading. OUTDATED, // The database file has not been modified after the previous // loading. UPDATED, // The database file is updated but the validation was failed // or the version is not supported by the current system. INVALID, }; public readonly MDatabaseTag Tag; private MDatabaseLoader Loader; private object ExtraInfo; private MDBType DBType; private MDBStatus DBStatus; internal DateTime LoadedTime; internal MDatabaseInfo Info; public static string ApplicationDirectory; private MDatabase (MDatabaseTag tag, MDatabaseLoader loader, object extra_info) { Tag = tag; Loader = loader; ExtraInfo = extra_info; } private MDatabase (MDatabaseTag tag, string filename) { Tag = tag; Info = new MDatabaseInfo (); Info.Filename = new MText (filename); } private MDatabase (MPlist plist) { MSymbol[] tags = new MSymbol[4]; int i; for (i = 0; plist.IsSymbol; i++, plist = plist.Next) tags[i] = plist.Symbol; while (i < 4) tags[i++] = MSymbol.nil; Tag = new MDatabaseTag (tags[0], tags[1], tags[2], tags[3]); if (plist.IsMText) { Info.Filename = plist.Text; plist = plist.Next; } else if (plist.IsPlist) { MPlist p = plist.Plist; if (p.IsMText) Info.Filename = plist.Text; p = p.Next; if (! p.IsEmpty) { if (p.IsSymbol) Info.Format = p.Symbol; p = p.Next; if (! p.IsEmpty) { if (p.IsSymbol) Info.Schema = p.Symbol; p = p.Next; if (p.IsMText) Info.SchemaFile = p.Text; } } plist = plist.Next; } DBStatus = MDBStatus.OUTDATED;; Info.Version = 0; Info.Props = new MPlist (); foreach (MPlist pl in plist) { if (pl.IsPlist) { MPlist p = pl.Plist; if (p.IsSymbol && p.Symbol == Mversion) { Info.Version = parse_version (p.Next); if (M17n.Version < Info.Version) DBStatus = MDBStatus.DISABLED; } } Info.Props.Put (pl.Key, pl.Val); } } private static int parse_version (MPlist plist) { string[] str; int major, minor, release; if (! plist.IsMText) return 0xFFFFFF; str = plist.Text.ToString ().Split ('.'); if (str.Length != 3) return 0xFFFFFF; try { major = int.Parse (str[0]); } catch { return 0xFFFFFF; } try { minor = int.Parse (str[1]); } catch { return 0xFFFFFF; } try { release = int.Parse (str[2]); } catch { return 0xFFFFFF; } return ((major << 16) | (minor << 8) | release); } public static MDatabase Define (MDatabaseTag tag, MDatabaseLoader loader, object extra_info) { MDatabase db = MDatabase.Find (tag); if (db != null) { db.Loader = loader; db.ExtraInfo = extra_info; db.DBType = MDBType.EXPLICIT; db.DBStatus = MDBStatus.OUTDATED; db.Info = null; return db; } return new MDatabase (tag, loader, extra_info); } public static MDatabase Define (MDatabaseTag tag, string filename) { MDatabase db = MDatabase.Find (tag); if (db != null) { db.Loader = null; db.DBType = MDBType.EXPLICIT; db.DBStatus = MDBStatus.OUTDATED; db.Info = new MDatabaseInfo (); db.Info.Filename = new MText (filename); return db; } return new MDatabase (tag, filename); } static MDatabase () { string share_dir = (Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData)); string usr_dir = (Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData)); try { DBDirs[0] = new MDatabaseDir (Path.Combine (usr_dir, ".m17n.d")); } catch (ArgumentException) { DBDirs[0] = new MDatabaseDir (Path.Combine (usr_dir, "_m17n_d")); } DBDirs[1] = null; DBDirs[2] = new MDatabaseDir (Path.Combine (share_dir, "m17n")); } public static MDatabase Find (MDatabaseTag tag) { MDatabase db; return (DBDict.TryGetValue (tag, out db) ? db : null); } public object Load () { return (Loader != null ? Loader (Tag, ExtraInfo) : load (MSymbol.nil, MSymbol.nil)); } public object Load (MSymbol key, MSymbol stop) { if (Loader != null) return null; return load (key, stop); } private object load (MSymbol key, MSymbol stop) { LoadedTime = DateTime.UtcNow; return null; } } }