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 { public string Dirname; public DirectoryInfo Info; public DateTime LastScanned; public MDatabaseDir (string dirname) { Dirname = dirname; try { Info = new DirectoryInfo (dirname); } finally { Info = null; } } public bool StatusChanged { get { bool exists = Directory.Exists (Dirname); if (Info != null) { if (! exists) { Info = null; return true; } Info.Refresh (); return (LastScanned < Info.LastWriteTime); } else { if (exists) { Info = new DirectoryInfo (Dirname); return true; } return false; } } } public void UpdateStatus () { LastScanned = DateTime.Now; } } private class MDatabaseDefinition { private static readonly MSymbol Mversion; public MDatabaseTag Tag; public string Description; public MText Filename; public bool IsWildcard; public MSymbol Format; public MSymbol Schema; public MText SchemaFile; public MPlist props; public bool Supported; static MDatabaseDefinition () { Mversion = new MSymbol ("version"); } public MDatabaseDefinition (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) { Filename = plist.Text; plist = plist.Next; } else if (plist.IsPlist) { MPlist p = plist.Plist; if (p.IsMText) Filename = plist.Text; p = p.Next; if (! p.IsEmpty) { if (p.IsSymbol) Format = p.Symbol; p = p.Next; if (! p.IsEmpty) { if (p.IsSymbol) Schema = p.Symbol; p = p.Next; if (p.IsMText) SchemaFile = p.Text; } } plist = plist.Next; } Supported = true; props = new MPlist (); foreach (MPlist pl in plist) { if (pl.IsPlist) { MPlist p = pl.Plist; if (p.IsSymbol && p.Symbol == Mversion && ! check_version (p.Next)) Supported = false; props.Put (pl); } } } private bool check_version (MPlist plist) { string[] str; int major, minor, release; if (! plist.IsMText) return false; str = plist.Text.ToString ().Split ('.'); if (str.Length != 3) return false; try { major = int.Parse (str[0]); } catch { return false; } try { minor = int.Parse (str[1]); } catch { return false; } try { release = int.Parse (str[2]); } catch { return false; } return (M17N.MajorVersion > major || (M17N.MajorVersion == major && (M17N.MinorVersion > minor || (M17N.MinorVersion == minor && M17N.ReleaseNumber >= release)))); } } private static MDatabaseDir[] DBDirs = new MDatabaseDir[3]; private const string SystemDirectory = "/usr/share/m17n"; private static Dictionary DBDict = new Dictionary (); /// 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. AUTO_WILDCARD, /// The database was defined explicitely (by MDatabaseDefine). EXPLICIT }; /// 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. // If this is for a database directory, the directory is // readable but "mdb.dir" doesn't exist in it. INVALID }; public readonly MDatabaseTag Tag; private MDatabaseLoader Loader; private object ExtraInfo; private bool IsSystemDatabase; private DirectoryInfo Dir; private MText Filename; private FileInfo FileInfo; private FileInfo Validater; private int MajorVersion, MinorVersion, ReleaseNumber; private MDBType DBType; private MDBStatus DBStatus; private MSymbol Format; private MSymbol Schema; private DateTime Mtime; private DateTime Ltime; private MPlist Props; 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; Filename = new MText (filename); } 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; 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.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")); } internal static void Update () { } 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) { Ltime = DateTime.Now; return null; } } }