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 ListChangeTime; public MDatabaseDir (string dirname) { Dirname = dirname; DirChangeTime = ListChangeTime = DateTime.Now; } public void Refresh () { if (DirInfo != null) { if (Dirname != null && Directory.Exists (Dirname)) { DirInfo.Refresh (); if (DirChangeTime < DirInfo.LastWriteTime) DirChangeTime = DirInfo.LastWriteTime; } else { DirInfo = null; DirChangeTime = DateTime.Now; } } 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 { } } } } } public partial class MDatabase : IComparable { /// Identifier of a MDatabase. public struct Tag : IEquatable { private MSymbol[] Tags; public Tag (MSymbol tag0) { Tags = new MSymbol[4]; Tags[0] = tag0; Tags[1] = Tags[2] = Tags[3] = MSymbol.nil; } public Tag (MSymbol tag0, MSymbol tag1) { Tags = new MSymbol[4]; Tags[0] = tag0; Tags[1] = tag1; Tags[2] = Tags[3] = MSymbol.nil; } public Tag (MSymbol tag0, MSymbol tag1, MSymbol tag2) { Tags = new MSymbol[4]; Tags[0] = tag0; Tags[1] = tag1; Tags[2] = tag2; Tags[3] = MSymbol.nil; } public Tag (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3) { Tags = new MSymbol[4]; Tags[0] = tag0; Tags[1] = tag1; Tags[2] = tag2; Tags[3] = tag3; } 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[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 MPlist Loader (Tag tag, object extra_info); internal class MDatabaseInfo { // These come from the declartion plist of the database. internal string Description; internal string Filename; internal FileInfo Validater; internal int Version; internal MSymbol Format; internal MSymbol Schema; internal string SchemaFile; internal MPlist Props; public MDatabaseInfo () { Description = null; Format = Schema = MSymbol.nil; } 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) : this () { 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 = ("#"; } } // Dictionaries for normal databases. private static Dictionary> ndict = new Dictionary> (); // Dictionaries for databases of DBType WILDCARD private static Dictionary> wdict = new Dictionary> (); private static MDatabaseDir[] DBDirs = new MDatabaseDir[4]; private static DateTime LastUpdateTime = new DateTime (0); 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 TimeSpan CheckInterval = new TimeSpan (50000000); /// Type of database private enum MDBType { /// The database was defined automatically from one of mdb.dir /// files with no wildcard tag. AUTO, /// The database was defined automatically from one of mdb.dir /// files with a wildcard tag to define multiple databases /// of the same kind. MULTIPLE, /// The database was defined explicitely by MDatabase.Define /// to use the normal loader. EXPLICIT, /// The database was defined explicitely by MDatabase.Define /// to use a special loader. UNKNOWN, /// The database is for defining multiple databases of the /// same kind with a wildcard tag. WILDCARD, }; /// Status of database private enum MDBStatus { // 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 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 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 () { string share_dir = (Environment.GetFolderPath (Environment.SpecialFolder.CommonApplicationData)); string usr_dir = (Environment.GetFolderPath (Environment.SpecialFolder.ApplicationData)); try { 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[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[2] = new MDatabaseDir (value); update_all (true); } } // 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 = 1; i < 4; i++) if (DBDirs[i].Dirname != null) { 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 Dump () { update_all (false); Console.WriteLine ("[DBDirs]"); for (int i = 1; i < 4; i++) if (DBDirs[i].Dirname != null) { 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"); } else 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 static void register (int list_idx, int dir_idx, Tag tag, MDatabaseInfo info) { Dictionary> dict = tag.HasWildcard ? wdict : ndict; List mdbs; MDatabase mdb; 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 List (1); dict.Add (tag, mdbs); } 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) { this.tag = tag; this.loader = loader; DBType = MDBType.UNKNOWN; DBStatus = MDBStatus.READY; DirIndex = 0; ListIndex = 0; ExtraInfo = extra_info; register (this); } public MDatabase (Tag tag, string filename) { this.tag = tag; DBType = MDBType.EXPLICIT; if (Path.IsPathRooted (filename)) { DirIndex = 0; if (File.Exists (filename)) DBStatus = MDBStatus.READY; else DBStatus = MDBStatus.INVALID; } else { DirIndex = -1; DBStatus = MDBStatus.NOT_READY; } ListIndex = 0; Info = new MDatabaseInfo (); Info.Filename = filename; register (this); } 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 DBStatus = MDBStatus.NOT_READY; if (Info.Format == Mchar_table) MCharProp.Define (tag[2], this); } public override String ToString () { string str = ("#"; } // Update (or disable) databases defined by "mdb.dir" in // DBDirs[list_idx]. private static void update_list (int list_idx) { 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; 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); } } } // Update (or disable) databases in DBDirs[dir_idx]. private static void update_dir (int dir_idx) { 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 bool parse_plist_header (FileInfo fileinfo, MDatabase mdb, out Tag tag, out MDatabaseInfo info) { MPlist plist = null; tag = new Tag (MSymbol.nil); info = null; using (FileStream stream = fileinfo.OpenRead ()) { try { plist = new MPlist (stream, 1); } catch { } } 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 ()) { 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 static void register_files (string dir, int dir_idx, MDatabase mdb) { 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 static void maybe_expand_wildcard (Tag tag) { 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) { List mdbs; MDatabase mdb = null; if (tag.HasWildcard) throw new ArgumentException ("Wildcard not allowed: " + tag); if (ndict.TryGetValue (tag, out mdbs)) { 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_all (false) && mdb != null) return mdb; maybe_expand_wildcard (tag); if (! ndict.TryGetValue (tag, out mdbs)) return null; for (int i = 0; i < mdbs.Count; i++) { mdb = mdbs[i]; mdb.update_status (); if (mdb.DBStatus == MDBStatus.READY) return mdb; } return null; } public static List List (Tag tag) { List list = new List (); update_all (false); maybe_expand_wildcard (tag); if (tag.HasWildcard) { foreach (KeyValuePair> kv in ndict) if (kv.Key.Match (tag)) foreach (MDatabase mdb in kv.Value) { mdb.update_status (); if (mdb.DBStatus == MDBStatus.READY) { list.Add (mdb); break; } } } else { List mdbs; if (ndict.TryGetValue (tag, out mdbs)) foreach (MDatabase mdb in mdbs) { mdb.update_status (); if (mdb.DBStatus == MDBStatus.READY) { list.Add (mdb); break; } } } return list; } private FileStream get_stream () { if (DBStatus != MDBStatus.READY) { LastLoadStatus = LoadStatus.NotAvailable; return null; } FileStream stream = null; try { stream = FileInfo.OpenRead (); } catch { LastLoadStatus = LoadStatus.NotReadable; } return stream; } public MPlist Load () { if (loader != null) return loader (tag, ExtraInfo); if (Info.Format != MSymbol.plist) throw new Exception ("Not a plist database"); 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 MPlist Load (MSymbol key, MSymbol stop) { if (loader != null) throw new Exception ("Partial load is impossible"); if (Info.Format != MSymbol.plist) throw new Exception ("Not a plist database"); FileStream stream = get_stream (); if (stream == null) return null; try { MPlist plist = new MPlist (stream, key, stop); LastLoaded = DateTime.Now; return plist; } catch { LastLoadStatus = LoadStatus.InvalidContents; return null; } finally { stream.Dispose (); } } public MPlist Load (MSymbol stop) { if (loader != null) throw new Exception ("Partial load is impossible"); if (Info.Format != MSymbol.plist) throw new Exception ("Not a plist database"); FileStream stream = get_stream (); if (stream == null) return null; try { MPlist plist = new MPlist (stream, stop); LastLoaded = DateTime.Now; return plist; } catch (Exception e) { Console.WriteLine (e); LastLoadStatus = LoadStatus.InvalidContents; return null; } finally { stream.Dispose (); } } private XmlTextReader get_reader (XmlDocument doc) { XmlTextReader reader; try { if (doc.NameTable != null) reader = new XmlTextReader (FileInfo.FullName, doc.NameTable); else reader = new XmlTextReader (FileInfo.FullName); reader.WhitespaceHandling = WhitespaceHandling.None; } catch { LastLoadStatus = LoadStatus.NotReadable; reader = null; } return reader; } public bool Load (XmlDocument doc) { if (Info.Format != Mxml) throw new Exception ("Not an XML database"); XmlTextReader reader = get_reader (doc); if (reader == null) return false; try { doc.Load (reader); LastLoaded = DateTime.Now; return true; } catch (Exception e) { Console.WriteLine (e); LastLoadStatus = LoadStatus.InvalidContents; return false; } finally { reader.Close (); } } public bool Load (XmlDocument doc, MSymbol key, MSymbol stop) { if (Info.Format != Mxml) throw new Exception ("Not an XML database"); XmlTextReader reader = get_reader (doc); if (reader == null) return false; 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); return true; } catch (Exception e) { Console.WriteLine (e); return false; } finally { reader.Close (); } } public bool Load (XmlDocument doc, MSymbol stop) { if (Info.Format != Mxml) throw new Exception ("Not an XML database"); XmlTextReader reader = get_reader (doc); if (reader == null) return false; 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) node = doc.DocumentElement.InsertAfter (doc.ReadNode (reader), node); return true; } catch (Exception e) { Console.WriteLine (e); return false; } finally { reader.Close (); } } public XmlNode Load (XmlDocument doc, string id, params string[] nodes) { if (Info.Format != Mxml) throw new Exception ("Not an XML database"); XmlTextReader reader = get_reader (doc); if (reader == null) return null; try { int len = nodes.Length; 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; 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 (); } return top; } catch (Exception e) { Console.WriteLine (e); return null; } finally { reader.Close (); } } /// 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); } } }