X-Git-Url: http://git.chise.org/gitweb/?p=m17n%2Fm17n-lib-cs.git;a=blobdiff_plain;f=MDatabase.cs;h=5779783495962cd4daa67fb1eb2dabe9992bbae3;hp=dca37f6dab434270cfdd2733331e3e4eb8bf64d4;hb=139bdc3719eee81aa203e40f40c612fb799b8ed4;hpb=6bcb07f73a9038a659d334bf0d553984d335b691 diff --git a/MDatabase.cs b/MDatabase.cs index dca37f6..5779783 100644 --- a/MDatabase.cs +++ b/MDatabase.cs @@ -1,108 +1,155 @@ using System; +using System.Collections; using System.Collections.Generic; using System.IO; +using System.Xml; + using M17N; using M17N.Core; namespace M17N.Core { + internal class MGlob + { + static readonly char sep = Path.DirectorySeparatorChar; + + public static void FileList (ref List files, + string dir, string pattern) + { + int len = pattern.Length; + + if (Path.IsPathRooted (pattern)) + { + int headsep = 0; + int i; + + for (i = 1; i < len && (pattern[i] != '*' && pattern[i] != '?'); i++) + if (pattern[i] == sep) + headsep = i; + if (i == len) + { + if (File.Exists (pattern)) + files.Add (new FileInfo (pattern)); + return; + } + dir = pattern.Substring (0, headsep); + pattern = pattern.Substring (headsep + 1); + } + else + { + if (dir == null) + dir = Directory.GetCurrentDirectory (); + } + if (Directory.Exists (dir)) + list (ref files, new DirectoryInfo (dir), pattern); + } + + private static void list (ref List files, + DirectoryInfo dirinfo, string pattern) + { + int len = pattern.Length; + int i; + + M17n.DebugPrint ("Listing {0} in {1} ...", pattern, dirinfo); + for (i = 0; i < len && pattern[i] != sep; i++); + try { + if (i == len) + { + FileInfo[] listing = dirinfo.GetFiles (pattern); + i = listing.Length; + foreach (FileInfo elt in listing) + files.Add (elt); + } + else + { + string tail = pattern.Substring (i + 1); + pattern = pattern.Substring (0, i); + DirectoryInfo[] listing = dirinfo.GetDirectories (pattern); + + i = listing.Length; + foreach (DirectoryInfo elt in listing) + list (ref files, elt, tail); + } + } catch { + } + M17n.DebugPrint (" found {0} files\n", i); + } + } + internal class MDatabaseDir { private const string ListFileName = "mdb.dir"; public string Dirname; public DirectoryInfo DirInfo; + public DateTime DirChangeTime; public FileInfo ListInfo; - public DateTime LastScanned; + public DateTime ListChangeTime; public MDatabaseDir (string dirname) { Dirname = dirname; - if (dirname != null) - { - try { - DirInfo = new DirectoryInfo (dirname); - try { - ListInfo = DirInfo.GetFiles (ListFileName)[0]; - } catch { - ListInfo = null; - } - } catch { - DirInfo = null; - ListInfo = null; - } - } + DirChangeTime = ListChangeTime = DateTime.Now; } - public bool CheckStatus () + public void Refresh () { if (DirInfo != null) { - try { - DirInfo.Refresh (); - } catch { - DirInfo = null; - ListInfo = null; - LastScanned = new DateTime (0); - return true; - } - if (ListInfo != null) - try { - ListInfo.Refresh (); - } catch { - ListInfo = null; - return true; + if (Dirname != null && Directory.Exists (Dirname)) + { + DirInfo.Refresh (); + if (DirChangeTime < DirInfo.LastWriteTime) + DirChangeTime = DirInfo.LastWriteTime; } else { - try { - ListInfo = DirInfo.GetFiles (ListFileName)[0]; - return true; - } catch { - ListInfo = null; - } + DirInfo = null; + DirChangeTime = DateTime.Now; } - return (LastScanned < DirInfo.LastWriteTime - || (ListInfo != null - && LastScanned < ListInfo.LastWriteTime)); } else { if (Dirname != null && Directory.Exists (Dirname)) { DirInfo = new DirectoryInfo (Dirname); + DirChangeTime = DateTime.Now; + } + } + if (DirInfo == null) + { + if (ListInfo != null) + { + ListInfo = null; + ListChangeTime = DateTime.Now; + } + } + else + { + if (ListInfo != null) + { + ListInfo.Refresh (); + if (ListChangeTime < ListInfo.LastWriteTime) + ListChangeTime = ListInfo.LastWriteTime; + } + else + { try { ListInfo = DirInfo.GetFiles (ListFileName)[0]; + ListChangeTime = DateTime.Now; } 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); - } } - public class MDatabase + public partial class MDatabase : IComparable { - /// Tags to identify a MDatabase. - public struct Tag + /// Identifier of a MDatabase. + public struct Tag : IEquatable { - public MSymbol[] Tags; + private MSymbol[] Tags; public Tag (MSymbol tag0) { @@ -128,71 +175,207 @@ namespace M17N.Core Tags[0] = tag0; Tags[1] = tag1; Tags[2] = tag2; Tags[3] = tag3; } - public bool Match (Tag tag) + public Tag (ref MPlist plist) + { + Tags = new MSymbol[4]; + + for (int i = 0; i < 4; i++) + { + if (plist.IsSymbol) + { + Tags[i] = plist.Symbol; + plist = plist.Next; + } + else + Tags[i] = MSymbol.nil; + } + } + + public bool Equals (Tag tag) { for (int i = 0; i < 4; i++) - { - if (tag.Tags[i] == Mwildcard || Tags[i] == Mwildcard) - return true; - if (tag.Tags[i] != Tags[i]) - return false; - } + if (tag[i] != Tags[i]) + return false; return true; } + public override int GetHashCode () + { + return (Tags[0].GetHashCode () ^ Tags[1].GetHashCode () + ^ Tags[2].GetHashCode () ^ Tags[3].GetHashCode ()); + } + public override string ToString () { return ("<" + Tags[0] + "," + Tags[1] + "," + Tags[2] + "," + Tags[3] + ">"); } + + public MSymbol this[int i] + { + set { Tags[i] = value; } + get { return Tags[i]; } + } + + public bool Match (Tag tag) + { + for (int i = 0; i < 4; i++) + { + if (tag[i] == Mwildcard || Tags[i] == Mwildcard) + return true; + if (tag[i] != Tags[i]) + return false; + } + return true; + } + + public bool HasWildcard { + get { + for (int i = 0; i < 4; i++) + if (Tags[i] == Mwildcard) + return true; + return false; + } + } } public delegate object Loader (Tag tag, object extra_info); - internal class MDatabaseInfo { - // -2: absolute, -1: unknown 0: DBDirs[0], 1: DBDirs[1], 2: DBDirs[2] - internal int DirIndex; + internal class MDatabaseInfo + { + // These come from the declartion plist of the database. internal string Description; internal string Filename; - internal FileInfo FileInfo; internal FileInfo Validater; internal int Version; internal MSymbol Format; internal MSymbol Schema; internal string SchemaFile; - internal DateTime ModifiedTime; internal MPlist Props; public MDatabaseInfo () { Format = Schema = MSymbol.nil; - DirIndex = -1; + } + + 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 MDatabaseInfo (MPlist plist) + { + Format = MSymbol.nil; + if (plist.IsMText) + { + Format = MSymbol.plist; + Filename = plist.Text.ToString (); + plist = plist.Next; + } + else if (plist.IsPlist) + { + MPlist p = plist.Plist; + + if (p.IsMText) + { + Filename = p.Text.ToString (); + p = p.Next; + } + if (p.IsSymbol) + { + Format = p.Symbol; + p = p.Next; + } + if (p.IsSymbol) + { + Schema = p.Symbol; + p = p.Next; + } + if (p.IsMText) + SchemaFile = p.Text.ToString (); + plist = plist.Next; + } + + Version = 0; + Props = new MPlist (); + foreach (MPlist pl in plist) + { + if (pl.IsPlist) + { + MPlist p = pl.Plist; + + if (p.IsSymbol && p.Symbol == Mversion) + Version = parse_version (p.Next); + else + Props.Put (pl.Key, pl.Val); + } + else if (pl.IsSymbol) + { + MPlist p = new MPlist (); + p.Add (MSymbol.symbol, pl.Symbol); + p.Add (MSymbol.symbol, MSymbol.t); + Props.Put (MSymbol.plist, p); + } + } + } + + public void Merge (MDatabaseInfo src) + { + if (Validater == null) + Validater = src.Validater; + if (Version == 0) + Version = src.Version; + if (Format == MSymbol.nil) + Format = src.Format; + if (Schema == MSymbol.nil) + Schema = src.Schema; + if (SchemaFile == null) + SchemaFile = src.SchemaFile; + foreach (MPlist p in src.Props) + if (Props.Assq (p.Plist.Symbol) == null) + Props.Push (p.Key, p.Val); } public override string ToString () { - string str = ("#"; } } - private static Dictionary DBDict - = new Dictionary (); + // Dictionaries for normal databases. + private static Dictionary> ndict + = new Dictionary> (); + + // Dictionaries for databases of DBType WILDCARD + private static Dictionary> wdict + = new Dictionary> (); - private static Dictionary DBDictMulti - = new Dictionary (); + private static MDatabaseDir[] DBDirs = new MDatabaseDir[4]; - private static MDatabaseDir[] DBDirs = new MDatabaseDir[3]; + private static DateTime LastUpdateTime = new DateTime (0); - private static readonly MSymbol Mversion = MSymbol.Of ("version"); + private static readonly MSymbol Mversion = "version"; + private static readonly MSymbol Mwildcard = "*"; + private static readonly MSymbol Mchar_table = "char-table"; + private static readonly MSymbol Mcharset = "charset"; + private static readonly MSymbol Mxml = "xml"; - private static readonly MSymbol Mwildcard = MSymbol.Of ("*"); - private static readonly MSymbol Mchar_table = MSymbol.Of ("char-table"); - private static readonly MSymbol Mcharset = MSymbol.Of ("charset"); + private static TimeSpan CheckInterval = new TimeSpan (50000000); /// Type of database private enum MDBType @@ -218,28 +401,49 @@ namespace M17N.Core /// 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". + // The database file has not yet been decided, or is not yet + // expanded if DBType is WILDCARD. + NOT_READY, + // The database file was decided, or has been expanded if + // DBType is WILDCARD. + READY, + // The database is disabled. It means that the database file + // is not readable, the version is not supported by the + // current system, or the validation was failed. 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. + // The database is deleted by the modificaiton of mdb.dir, or + // is overwritten by a new explicit definition. INVALID, }; public Tag tag; private Loader loader; private object ExtraInfo; + // Directory of the database file. + // -1:unknown, 0:absolute, 1:DBDirs[1], 2:DBDirs[2], 3:DBDirs[3] + private int DirIndex; + // Directory of the mdb.dir defining the database file. + // 0: EXPLICIT or UNKNOWN, 1:DBDirs[1], 2:DBDirs[2], 3:DBDirs[3] + private int ListIndex; private MDBType DBType; private MDBStatus DBStatus; - internal DateTime LoadedTime; internal MDatabaseInfo Info; + // File in which the database contents is stored. This is null + // when DBStatus is NOT_READY. + internal FileInfo FileInfo; + // When the database file is loaded last. + internal DateTime LastLoaded = DateTime.Now; + + public enum LoadStatus + { + None, + InvalidLoadMethod, + NotAvailable, + NotReadable, + InvalidContents, + }; + + public LoadStatus LastLoadStatus = LoadStatus.None; static MDatabase () { @@ -249,63 +453,155 @@ namespace M17N.Core (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")); + string dir = Environment.GetEnvironmentVariable ("M17NDIR"); + DBDirs[1] = new MDatabaseDir (dir); + } catch { + try { + DBDirs[1] = new MDatabaseDir (Path.Combine (usr_dir, ".m17n.d")); + } catch (ArgumentException) { + DBDirs[1] = new MDatabaseDir (Path.Combine (usr_dir, "_m17n_d")); + } } - DBDirs[1] = new MDatabaseDir (null); - DBDirs[2] = new MDatabaseDir (Path.Combine (share_dir, "m17n")); + DBDirs[2] = new MDatabaseDir (null); + DBDirs[3] = new MDatabaseDir (Path.Combine (share_dir, "m17n")); + update_all (true); } public static string ApplicationDir - { get { return (DBDirs[1].Dirname); } - set { DBDirs[1].Dirname = value; DBDirs[1].CheckStatus (); } } + { + get { return (DBDirs[1].Dirname); } + set { DBDirs[2] = new MDatabaseDir (value); update_all (true); } + } - private static bool update_database_directories () + // Update all listing and directories. Return true iff some are + // really updated. + private static bool update_all (bool force) { + if (! force && DateTime.Now - LastUpdateTime < CheckInterval) + return false; + bool updated = false; - for (int i = 0; i < 3; i++) - if (DBDirs[i].CheckStatus ()) + for (int i = 1; i < 4; i++) + if (DBDirs[i].Dirname != null) { - delete_databases (i); - if (DBDirs[i].ListInfo != null) - update_databases (i); - updated = true; + DBDirs[i].Refresh (); + if (LastUpdateTime < DBDirs[i].ListChangeTime) + { + update_list (i); + updated = true; + } + if (LastUpdateTime < DBDirs[i].DirChangeTime) + { + update_dir (i); + updated = true; + } } + if (updated) + LastUpdateTime = DateTime.Now; return updated; } - public static void ListDirs () + public static void Dump () { - update_database_directories (); - for (int i = 0; i < 3; i++) + update_all (false); + Console.WriteLine ("[DBDirs]"); + for (int i = 1; i < 4; i++) if (DBDirs[i].Dirname != null) { - Console.Write ("{0}:{1}", i, DBDirs[i].Dirname); if (DBDirs[i].DirInfo != null) { + Console.Write ("{0}:{1}", i, DBDirs[i].DirInfo.FullName); if (DBDirs[i].ListInfo != null) Console.WriteLine (" {0}", DBDirs[i].ListInfo); else - Console.WriteLine (".. no mdb.dir"); + Console.WriteLine (" .. no mdb.dir"); } else - Console.WriteLine (".. not exist"); + Console.WriteLine ("{0}:{1} .. not exist", i, DBDirs[i].Dirname); } + + Console.WriteLine ("[WDICT]"); + foreach (KeyValuePair> kv in wdict) + foreach (MDatabase mdb in kv.Value) + Console.WriteLine (mdb); + + Console.WriteLine ("[NDICT]"); + foreach (KeyValuePair> kv in ndict) + foreach (MDatabase mdb in kv.Value) + Console.WriteLine (mdb); + } + + private static void register (MDatabase mdb) + { + Dictionary> dict + = mdb.DBType == MDBType.WILDCARD ? wdict : ndict; + List mdbs; + + if (dict.TryGetValue (mdb.tag, out mdbs)) + { + for (int i = 0; i < mdbs.Count; i++) + if (mdbs[i].ListIndex == mdb.ListIndex) + { + mdbs[i].DBStatus = MDBStatus.INVALID; + mdbs[i] = mdb; + return; + } + mdbs.Add (mdb); + } + else + { + mdbs = new List (1); + mdbs.Add (mdb); + dict.Add (mdb.tag, mdbs); + } } - private void register (int priority) + private static void register (int list_idx, int dir_idx, + Tag tag, MDatabaseInfo info) { - Dictionary dict - = DBType == MDBType.WILDCARD ? DBDictMulti : DBDict; - MDatabase[] mdbs; + Dictionary> dict + = tag.HasWildcard ? wdict : ndict; + List mdbs; + MDatabase mdb; - if (! dict.TryGetValue (tag, out mdbs)) + if (dict.TryGetValue (tag, out mdbs)) + for (int i = 0; i < mdbs.Count; i++) + { + mdb = mdbs[i]; + if (mdb.ListIndex == list_idx && mdb.DirIndex >= dir_idx) + { + mdb.DirIndex = dir_idx; + if (dict == wdict) + { + mdb.DBType = MDBType.WILDCARD; + mdb.DBStatus = MDBStatus.NOT_READY; + } + else if (dir_idx == -1) + { + mdb.DBType = MDBType.AUTO; + mdb.DBStatus = MDBStatus.NOT_READY; + } + else + { + mdb.DBType = MDBType.MULTIPLE; + mdb.DBStatus = MDBStatus.READY; + } + mdb.Info = info; + if (mdb.DBStatus == MDBStatus.INVALID) + M17n.DebugPrint ("registering: {0}\n", mdb); + else + M17n.DebugPrint ("updating: {0}\n", mdb); + return; + } + } + else { - mdbs = new MDatabase[4]; + mdbs = new List (1); dict.Add (tag, mdbs); } - mdbs[priority] = this; + mdb = new MDatabase (list_idx, dir_idx, tag, info); + M17n.DebugPrint ("registering: {0}\n", mdb); + mdbs.Add (mdb); } public MDatabase (Tag tag, Loader loader, object extra_info) @@ -313,192 +609,322 @@ namespace M17N.Core this.tag = tag; this.loader = loader; DBType = MDBType.UNKNOWN; - DBStatus = MDBStatus.UPDATED; + DBStatus = MDBStatus.READY; + DirIndex = 0; + ListIndex = 0; ExtraInfo = extra_info; - this.register (0); + register (this); } public MDatabase (Tag tag, string filename) { this.tag = tag; DBType = MDBType.EXPLICIT; - DBStatus = MDBStatus.OUTDATED; - Info = new MDatabaseInfo (); - Info.Filename = filename; - Info.DirIndex = Path.IsPathRooted (filename) ? -2 : -1; - this.register (0); - } - - private MDatabase (MPlist plist, int priority) - { - tag = new Tag (MSymbol.nil); - DBType = MDBType.AUTO; - DBStatus = MDBStatus.OUTDATED; - for (int i = 0; plist.IsSymbol; i++, plist = plist.Next) + if (Path.IsPathRooted (filename)) { - if (DBType == MDBType.WILDCARD) - tag.Tags[i] = MSymbol.nil; + DirIndex = 0; + if (File.Exists (filename)) + DBStatus = MDBStatus.READY; else - { - tag.Tags[i] = plist.Symbol; - if (tag.Tags[i] == Mwildcard) - DBType = MDBType.WILDCARD; - } + DBStatus = MDBStatus.INVALID; } - - Info = new MDatabaseInfo (); - if (tag.Tags[0] == Mchar_table || tag.Tags[0] == Mcharset) - Info.Format = tag.Tags[0]; else - Info.Format = MSymbol.plist; - if (plist.IsMText) { - Info.Filename = plist.Text.ToString (); - plist = plist.Next; + DirIndex = -1; + DBStatus = MDBStatus.NOT_READY; } - else if (plist.IsPlist) - { - MPlist p = plist.Plist; + ListIndex = 0; + Info = new MDatabaseInfo (); + Info.Filename = filename; + register (this); + } - if (p.IsMText) - Info.Filename = plist.Text.ToString (); - 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.ToString (); - } - } - plist = plist.Next; - } + private MDatabase (int list_idx, int dir_idx, Tag tag, MDatabaseInfo info) + { + this.tag = tag; + this.Info = info; + DBType = this.tag.HasWildcard ? MDBType.WILDCARD : MDBType.AUTO; + if (tag[0] == Mchar_table || tag[0] == Mcharset) + Info.Format = tag[0]; + ListIndex = list_idx; + DirIndex = dir_idx; + if (Path.IsPathRooted (Info.Filename)) + DBStatus = MDBStatus.READY; else - throw new Exception ("Invalid source definition:" + plist); - - Info.DirIndex = Path.IsPathRooted (Info.Filename) ? -2 : -1; - 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); - } - this.register (priority); + DBStatus = MDBStatus.NOT_READY; + if (Info.Format == Mchar_table) + MCharProp.Define (tag[2], this); } public override String ToString () { - string str = "#"; } - private static int parse_version (MPlist plist) + // Update (or disable) databases defined by "mdb.dir" in + // DBDirs[list_idx]. + private static void update_list (int list_idx) { - string[] str; - int major, minor, release; + M17n.DebugPrint ("Updating list: {0}\n", list_idx); + // At first disable all target databases. + foreach (KeyValuePair> kv in wdict) + foreach (MDatabase mdb in kv.Value) + if (mdb.ListIndex == list_idx) + { + M17n.DebugPrint ("deleting: {0}\n", mdb); + mdb.DBStatus = MDBStatus.INVALID; + break; + } + foreach (KeyValuePair> kv in ndict) + foreach (MDatabase mdb in kv.Value) + if (mdb.ListIndex == list_idx) + { + M17n.DebugPrint ("deleting: {0}\n", mdb); + mdb.DBStatus = MDBStatus.INVALID; + break; + } + + FileInfo dblist = DBDirs[list_idx].ListInfo; + if (dblist == null) + return; - 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); + MPlist plist = null; + using (FileStream stream = File.OpenRead (dblist.FullName)) + { + plist = new MPlist (stream); + } + if (plist == null) + return; + foreach (MPlist pl in plist) + if (pl.IsPlist) + { + try + { + MPlist p = pl.Plist; + Tag tag = new Tag (ref p); + MDatabaseInfo info = new MDatabaseInfo (p); + int dir_idx = Path.IsPathRooted (info.Filename) ? 0 : -1; + register (list_idx, dir_idx, tag, info); + } + catch (Exception e) + { + Console.WriteLine (e.Message + ": " + pl.Plist); + } + } } - private static void delete_databases (int list_idx) + // Update (or disable) databases in DBDirs[dir_idx]. + private static void update_dir (int dir_idx) { - foreach (KeyValuePair item in DBDict) - item.Value[list_idx + 1] = null; - foreach (KeyValuePair item in DBDictMulti) - item.Value[list_idx + 1] = null; + M17n.DebugPrint ("Updating dir: {0}\n", dir_idx); + // Reset all databases in DBDirs[dir_idx]. + foreach (KeyValuePair> kv in ndict) + foreach (MDatabase mdb in kv.Value) + if (mdb.DirIndex >= dir_idx) + { + M17n.DebugPrint ("disabling: {0}\n", mdb); + mdb.DBStatus = MDBStatus.NOT_READY; + mdb.DirIndex = -1; + } + // Re-expand all WILDCARD databases in DBDirs[dir_idx]. + if (DBDirs[dir_idx].DirInfo != null) + foreach (KeyValuePair> kv in wdict) + foreach (MDatabase mdb in kv.Value) + if (mdb.DBStatus == MDBStatus.READY) + { + M17n.DebugPrint ("re-expanding: {0}\n", mdb); + register_files (DBDirs[dir_idx].Dirname, dir_idx, mdb); + } } - private static void update_databases (int list_idx) + private static bool parse_plist_header (FileInfo fileinfo, MDatabase mdb, + out Tag tag, out MDatabaseInfo info) { - MDatabaseDir dbdir = DBDirs[list_idx]; - FileInfo dblist = dbdir.ListInfo; MPlist plist = null; - using (FileStream stream = File.OpenRead (dblist.FullName)) + tag = new Tag (MSymbol.nil); + info = null; + using (FileStream stream = fileinfo.OpenRead ()) { - MStreamReader reader = new MStreamReader (stream); - plist = new MPlist (reader); + try { plist = new MPlist (stream, 1); } catch { } } - if (plist == null) - return; - foreach (MPlist pl in plist) + if (plist == null || ! plist.IsPlist) + return false; + plist = plist.Plist; + tag = new Tag (ref plist); + if (tag.HasWildcard || ! tag.Match (mdb.tag)) + return false; + info = new MDatabaseInfo (plist); + return true; + } + + private static bool parse_xml_header (FileInfo fileinfo, MDatabase mdb, + out Tag tag, out MDatabaseInfo info) + { + tag = new Tag (MSymbol.nil); + info = null; + using (FileStream stream = fileinfo.OpenRead ()) { - if (! pl.IsPlist) - continue; - try - { - new MDatabase (pl.Plist, list_idx + 1); - } - catch (Exception e) - { - Console.WriteLine (e.Message); - } + try { + MPlist plist = new MPlist (); + XmlTextReader reader = new XmlTextReader (stream); + + reader.WhitespaceHandling = WhitespaceHandling.None; + do { + reader.Read (); + } while (reader.NodeType != XmlNodeType.Element); + plist.Add (MSymbol.symbol, (MSymbol) reader.Name); + reader.Read (); + if (reader.NodeType == XmlNodeType.Element && reader.Name == "tags") + { + reader.Read (); + while (reader.NodeType == XmlNodeType.Element) + { + reader.Read (); + plist.Add (MSymbol.symbol, (MSymbol) reader.Value); + reader.Read (); + reader.Read (); + } + tag = new Tag (ref plist); + if (tag.HasWildcard || ! tag.Match (mdb.tag)) + return false; + info = new MDatabaseInfo (plist); + return true; + } + } catch (Exception e) { + Console.WriteLine ("error {0}", e); + } } + return false; } - private void expand_wildcard (int priority) + private static void register_files (string dir, int dir_idx, MDatabase mdb) { - if (Info.DirIndex == -2) + int list_idx = mdb.ListIndex; + List files = new List (); + MGlob.FileList (ref files, dir, mdb.Info.Filename); + foreach (FileInfo fileinfo in files) { - + Tag tag; + MDatabaseInfo info; + + if (mdb.Info.Format == MSymbol.plist + ? parse_plist_header (fileinfo, mdb, out tag, out info) + : parse_xml_header (fileinfo, mdb, out tag, out info)) + { + info.Merge (mdb.Info); + if (Path.IsPathRooted (mdb.Info.Filename)) + info.Filename = fileinfo.FullName; + else + info.Filename = fileinfo.Name; + register (list_idx, dir_idx, tag, info); + } } + } + + private void expand_wildcard () + { + M17n.DebugPrint ("expanding: {0}\n", this); + if (DirIndex == 0) + register_files (null, DirIndex, this); + else + for (int i = 1; i < 4; i++) + if (DBDirs[i].DirInfo != null) + register_files (DBDirs[i].DirInfo.FullName, i, this); + DBStatus = MDBStatus.READY; } - private void update_status () + private static void maybe_expand_wildcard (Tag tag) { - if (Info.DirIndex == -2) + foreach (KeyValuePair> kv in wdict) { + M17n.DebugPrint ("expand check: {0}\n", kv.Key); + if (kv.Key.Match (tag)) + { + foreach (MDatabase mdb in kv.Value) + { + if (mdb.DBStatus == MDBStatus.NOT_READY) + mdb.expand_wildcard (); + } + } } } + // Update the status. Return true iff the database file is + // readable but changed. + + private bool update_status () + { + if (DBType == MDBType.UNKNOWN) + return true; + update_all (false); + if (DBStatus == MDBStatus.INVALID) + return false; + if (DBStatus != MDBStatus.NOT_READY) + { + try { + FileInfo.Refresh (); + } catch { + DBStatus = MDBStatus.INVALID; + return false; + } + if (LastLoaded >= FileInfo.LastWriteTime) + return false; + DBStatus = MDBStatus.READY; + return true; + } + for (int i = 1; i < 4; i++) + if (DBDirs[i] != null && DBDirs[i].Dirname != null) + { + string filename = Path.Combine (DBDirs[i].Dirname, Info.Filename); + if (File.Exists (filename)) + { + FileInfo = new FileInfo (filename); + DirIndex = i; + DBStatus = MDBStatus.READY; + return true; + } + } + return false; + } + public static MDatabase Find (Tag tag) { - MDatabase[] mdbs; + List mdbs; MDatabase mdb = null; - if (DBDict.TryGetValue (tag, out mdbs)) + if (tag.HasWildcard) + throw new ArgumentException ("Wildcard not allowed: " + tag); + + if (ndict.TryGetValue (tag, out mdbs)) { - if (mdbs[0] != null) - return mdbs[0]; - for (int i = 1; i < 4; i++) - if ((mdb = mdbs[i]) != null) - break; + mdbs.Sort (); + for (int i = 0; i < mdbs.Count; i++) + { + mdb = mdbs[i]; + if (mdb.ListIndex == 0) + return mdb; + if (mdb.DBStatus == MDBStatus.READY) + break; + } } - if (! update_database_directories ()) + if (! update_all (false) && mdb != null) return mdb; - if (! DBDict.TryGetValue (tag, out mdbs)) + maybe_expand_wildcard (tag); + if (! ndict.TryGetValue (tag, out mdbs)) return null; - for (int i = 1; i < 4; i++) - if (mdbs[i] != null) - return mdbs[i]; + for (int i = 0; i < mdbs.Count; i++) + { + mdb = mdbs[i]; + mdb.update_status (); + if (mdb.DBStatus == MDBStatus.READY) + return mdb; + } return null; } @@ -506,68 +932,279 @@ namespace M17N.Core { List list = new List (); - update_database_directories (); - foreach (KeyValuePair item in DBDictMulti) - if (item.Key.Match (tag)) - for (j = 0; j < 4; j++) - if (item.Value[j] != null) - if (item.Value[j].DBStatus == MDBStatus.OUTDATED) - item.Value[j].expand_wildcard (j); + update_all (false); + maybe_expand_wildcard (tag); - for (int i = 0; i < 4 && tag.Tags[i] == Mwildcard; i++); - if (i == 4) + if (tag.HasWildcard) { - // No wildcard. - if (DBDict.TryGetValue (tag, out mdbs)) - for (int j = 0; j < 4; j++) - if (mdbs[j] != null) + foreach (KeyValuePair> kv in ndict) + if (kv.Key.Match (tag)) + foreach (MDatabase mdb in kv.Value) { - mdbs[j].update_status (); - if (mdbs[j].DBStatus != MDBStatus.DISABLED) + mdb.update_status (); + if (mdb.DBStatus == MDBStatus.READY) { - list.Add (mdbs[j]); + list.Add (mdb); break; } } } else { - // With wildcard. We must scan all databases. - foreach (KeyValuePair item in DBDict) - if (item.Key.Match (tag)) - for (int j = 0; j < 4; j++) - if (item.Value[j] != null) + List mdbs; + if (ndict.TryGetValue (tag, out mdbs)) + foreach (MDatabase mdb in mdbs) + { + mdb.update_status (); + if (mdb.DBStatus == MDBStatus.READY) { - MDatabase mdb = item.Value[j]; - mdb.update_status (); - if (mdb.DBStatus != MDBStatus.DISABLED) - { - list.Add (mdb); - break; - } + list.Add (mdb); + break; } + } } return list; } + private FileStream get_stream () + { + if (loader != null + || (Info.Format != MSymbol.plist && Info.Format != Mxml)) + { + LastLoadStatus = LoadStatus.InvalidLoadMethod; + return null; + } + if (DBStatus != MDBStatus.READY) + { + LastLoadStatus = LoadStatus.NotAvailable; + return null; + } + + FileStream stream = null; + try { + stream = FileInfo.OpenRead (); + } catch { + LastLoadStatus = LoadStatus.NotReadable; + } + return stream; + } + public object Load () { - return (loader != null ? loader (tag, ExtraInfo) - : load (MSymbol.nil, MSymbol.nil)); + if (loader != null) + return loader (tag, ExtraInfo); + if (Info.Format == Mxml) + { + XmlDocument doc = new XmlDocument (); + try { + XmlReader reader = XmlReader.Create (FileInfo.FullName); + doc.Load (reader); + LastLoaded = DateTime.Now; + } catch (Exception e) { + Console.WriteLine (e); + LastLoadStatus = LoadStatus.InvalidContents; + } + return doc; + } + + FileStream stream = get_stream (); + if (stream == null) + return null; + MPlist plist = null; + try { + plist = new MPlist (stream); + LastLoaded = DateTime.Now; + } catch { + LastLoadStatus = LoadStatus.InvalidContents; + } finally { + stream.Dispose (); + } + return plist; } public object Load (MSymbol key, MSymbol stop) { - if (loader != null) + FileStream stream = get_stream (); + + if (stream == null) return null; - return load (key, stop); + if (Info.Format == Mxml) + { + XmlDocument doc = new XmlDocument (); + XmlTextReader reader = new XmlTextReader (stream); + + reader.WhitespaceHandling = WhitespaceHandling.None; + try { + reader.Read (); + while (reader.NodeType != XmlNodeType.Element) + reader.Read (); + doc.LoadXml ("<" + reader.Name + ">"); + reader.Read (); + XmlNode node = doc.DocumentElement; + while (reader.NodeType == XmlNodeType.Element + ? reader.Name != stop.Name + : reader.NodeType != XmlNodeType.EndElement) + if (reader.NodeType == XmlNodeType.Element + && reader.Name == key.Name) + node = doc.DocumentElement.InsertAfter (doc.ReadNode (reader), + node); + } finally { + reader.Close (); + stream.Dispose (); + } + return doc; + } + + MPlist plist = null; + try { + plist = new MPlist (stream, key, stop); + LastLoaded = DateTime.Now; + } catch { + LastLoadStatus = LoadStatus.InvalidContents; + } finally { + stream.Dispose (); + } + return plist; } - private object load (MSymbol key, MSymbol stop) + public object Load (MSymbol stop) { - LoadedTime = DateTime.UtcNow; + FileStream stream = get_stream (); - return null; + if (stream == null) + return null; + if (Info.Format == Mxml) + { + XmlDocument doc = new XmlDocument (); + XmlTextReader reader = new XmlTextReader (stream); + + reader.WhitespaceHandling = WhitespaceHandling.None; + try { + reader.Read (); + while (reader.NodeType != XmlNodeType.Element) + reader.Read (); + doc.LoadXml ("<" + reader.Name + ">"); + reader.Read (); + XmlNode node = null; + while (reader.NodeType == XmlNodeType.Element + ? reader.Name != stop.Name + : reader.NodeType != XmlNodeType.EndElement) + if (reader.NodeType == XmlNodeType.Element) + node = doc.DocumentElement.InsertAfter (doc.ReadNode (reader), + node); + } catch (Exception e) { + Console.WriteLine (e); + } finally { + reader.Close (); + stream.Dispose (); + } + return doc; + } + + MPlist plist = null; + try { + plist = new MPlist (stream, stop); + LastLoaded = DateTime.Now; + } catch (Exception e) { + Console.WriteLine (e); + LastLoadStatus = LoadStatus.InvalidContents; + } finally { + stream.Dispose (); + } + return plist; + } + + public XmlNode Load (string id, params string[] nodes) + { + FileStream stream = get_stream (); + if (stream == null) + return null; + if (Info.Format != Mxml) + throw new Exception ("Not an XML format"); + + XmlDocument doc = new XmlDocument (); + XmlTextReader reader = new XmlTextReader (stream); + int len = nodes.Length; + + reader.WhitespaceHandling = WhitespaceHandling.None; + do { + reader.Read (); + } while (reader.NodeType != XmlNodeType.Element); + + if (reader.Name != nodes[0]) + return null; + + string ns = reader.GetAttribute ("xmlns"); + XmlNode top = doc.CreateNode (XmlNodeType.Element, nodes[0], ns); + XmlNode node = top; + + try { + int i; + + for (i = 1; i + 1 < len; i++) + { + if (! reader.ReadToDescendant (nodes[i])) + return null; + node = node.InsertAfter (doc.CreateNode (XmlNodeType.Element, + nodes[i], ns), null); + } + if (! reader.ReadToDescendant (nodes[i])) + return null; + XmlNode ref_node = null; + while (reader.NodeType != XmlNodeType.EndElement) + { + if (reader.NodeType == XmlNodeType.Element) + { + if (reader.Name == nodes[i] + && (id == null || id == reader.GetAttribute ("id"))) + ref_node = node.InsertAfter (doc.ReadNode (reader), ref_node); + else + reader.Skip (); + } + else + reader.Read (); + } + + } catch (Exception e) { + Console.WriteLine (e); + } finally { + reader.Close (); + stream.Dispose (); + } + return top; + } + + /// Return a list of currently available database + /// directory names. + public static string[] DirectoryList () + { + List dirs = new List (); + + for (int i = 1; i < 4; i++) + if (DBDirs[i].Dirname != null) + dirs.Add (DBDirs[i].Dirname); + return dirs.ToArray (); + } + + public MSymbol Format { get { return Info.Format; } } + + public static bool Changed (DateTime time) + { + update_all (false); + return (time < LastUpdateTime); + } + + public bool NeedReload () + { + return update_status (); + } + + // For IComparable + public int CompareTo (MDatabase other) + { + return (ListIndex == other.ListIndex + ? DirIndex - other.DirIndex + : ListIndex - other.ListIndex); } } -} \ No newline at end of file +}