*** empty log message ***
[m17n/m17n-lib-cs.git] / MDatabase.cs
index a532b96..afefa61 100644 (file)
@@ -2,6 +2,8 @@ using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.IO;
+using System.Xml;
+
 using M17N;
 using M17N.Core;
 
@@ -48,11 +50,13 @@ namespace M17N.Core
       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);
          }
@@ -61,11 +65,14 @@ namespace M17N.Core
            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);
     }
   }
 
@@ -137,7 +144,7 @@ namespace M17N.Core
     }
   }
 
-  public class MDatabase : IComparable<MDatabase>
+  public partial class MDatabase : IComparable<MDatabase>
   {
     /// Identifier of a MDatabase.
     public struct Tag : IEquatable<Tag>
@@ -270,9 +277,10 @@ namespace M17N.Core
 
       public MDatabaseInfo (MPlist plist)
       {
-       Format = MSymbol.plist;
+       Format = MSymbol.nil;
        if (plist.IsMText)
          {
+           Format = MSymbol.plist;
            Filename = plist.Text.ToString ();
            plist = plist.Next;
          }
@@ -281,22 +289,22 @@ namespace M17N.Core
            MPlist p = plist.Plist;
 
            if (p.IsMText)
-             Filename = p.Text.ToString ();
-           p = p.Next;
-           if (! p.IsEmpty)
              {
-               if (p.IsSymbol)
-                 Format = p.Symbol;
+               Filename = p.Text.ToString ();
+               p = p.Next;
+             }
+           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.ToString ();
-                 }                 
              }
+           if (p.IsSymbol)
+             {
+               Schema = p.Symbol;
+               p = p.Next;
+             }
+           if (p.IsMText)
+             SchemaFile = p.Text.ToString ();
            plist = plist.Next;
          }
 
@@ -361,10 +369,13 @@ namespace M17N.Core
 
     private static DateTime LastUpdateTime = new DateTime (0);
 
-    private static readonly MSymbol Mversion = MSymbol.Of ("version");
-    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 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
@@ -401,11 +412,12 @@ namespace M17N.Core
        // 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..
+       // is overwritten by a new explicit definition.
        INVALID,
       };
 
     public Tag tag;
+    public NameTable name_table = new NameTable ();
     private Loader loader;
     private object ExtraInfo;
     // Directory of the database file.
@@ -417,10 +429,22 @@ namespace M17N.Core
     private MDBType DBType;
     private MDBStatus DBStatus;
     internal MDatabaseInfo Info;
-    // File in which the database contents is stored.
+    // File in which the database contents is stored.  This is null
+    // when DBStatus is NOT_READY.
     internal FileInfo FileInfo;
-    // When the database file is checked (or validated).
-    internal DateTime CheckedTime;
+    // 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 ()
     {
@@ -441,19 +465,23 @@ namespace M17N.Core
       }
       DBDirs[2] = new MDatabaseDir (null);
       DBDirs[3] = new MDatabaseDir (Path.Combine (share_dir, "m17n"));
-      update_all ();
+      update_all (true);
     }
 
     public static string ApplicationDir
     {
       get { return (DBDirs[1].Dirname); }
-      set { DBDirs[2] = new MDatabaseDir (value); update_all (); }
+      set { DBDirs[2] = new MDatabaseDir (value); update_all (true); }
     }
 
-    private static bool update_all ()
+    // Update all listing and directories.  Return true iff some are
+    // really updated.
+    private static bool update_all (bool force)
     {
-      bool updated = false;
+      if (! force && DateTime.Now - LastUpdateTime < CheckInterval)
+       return false;
 
+      bool updated = false;
       for (int i = 1; i < 4; i++)
        if (DBDirs[i].Dirname != null)
          {
@@ -469,13 +497,14 @@ namespace M17N.Core
                updated = true;
              }
          }
-      LastUpdateTime = DateTime.Now;
+      if (updated)
+       LastUpdateTime = DateTime.Now;
       return updated;
     }
 
     public static void Dump ()
     {
-      update_all ();
+      update_all (false);
       Console.WriteLine ("[DBDirs]");
       for (int i = 1; i < 4; i++)
        if (DBDirs[i].Dirname != null)
@@ -497,7 +526,7 @@ namespace M17N.Core
        foreach (MDatabase mdb in kv.Value)
          Console.WriteLine (mdb);
 
-      Console.WriteLine ("[NDITCT]");
+      Console.WriteLine ("[NDICT]");
       foreach (KeyValuePair<Tag, List<MDatabase>> kv in ndict)
        foreach (MDatabase mdb in kv.Value)
          Console.WriteLine (mdb);
@@ -514,7 +543,6 @@ namespace M17N.Core
          for (int i = 0; i < mdbs.Count; i++)
            if (mdbs[i].ListIndex == mdb.ListIndex)
              {
-               
                mdbs[i].DBStatus = MDBStatus.INVALID;
                mdbs[i] = mdb;
                return;
@@ -532,30 +560,45 @@ namespace M17N.Core
     private static void register (int list_idx, int dir_idx,
                                  Tag tag, MDatabaseInfo info)
     {
+      Dictionary<MDatabase.Tag, List<MDatabase>> dict
+       = tag.HasWildcard ? wdict : ndict;
       List<MDatabase> mdbs;
       MDatabase mdb;
 
-      if (ndict.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)
+           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);
-               mdb.DBType = dir_idx == -1 ? MDBType.AUTO : MDBType.MULTIPLE;
-               mdb.DBStatus = MDBStatus.NOT_READY;
-               mdb.DirIndex = dir_idx;
-               mdb.Info = info;
                return;
              }
          }
       else
        {
          mdbs = new List<MDatabase> (1);
-         ndict.Add (tag, mdbs);
+         dict.Add (tag, mdbs);
        }
       mdb = new MDatabase (list_idx, dir_idx, tag, info);
       M17n.DebugPrint ("registering: {0}\n", mdb);
@@ -581,7 +624,10 @@ namespace M17N.Core
       if (Path.IsPathRooted (filename))
        {
          DirIndex = 0;
-         DBStatus = MDBStatus.READY;
+         if (File.Exists (filename))
+           DBStatus = MDBStatus.READY;
+         else
+           DBStatus = MDBStatus.INVALID;
        }
       else
        {
@@ -599,14 +645,16 @@ namespace M17N.Core
       this.tag = tag;
       this.Info = info;
       DBType = this.tag.HasWildcard ? MDBType.WILDCARD : MDBType.AUTO;
-      if (this.tag[0] == Mchar_table || this.tag[0] == Mcharset)
-       Info.Format = this.tag[0];
+      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 () {
@@ -622,6 +670,7 @@ namespace M17N.Core
     // 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<Tag, List<MDatabase>> kv in wdict)
        foreach (MDatabase mdb in kv.Value)
@@ -647,8 +696,7 @@ namespace M17N.Core
       MPlist plist = null;
       using (FileStream stream = File.OpenRead (dblist.FullName))
        {
-         MStreamReader reader = new MStreamReader (stream);
-         plist = new MPlist (reader);
+         plist = new MPlist (stream);
        }
       if (plist == null)
        return;
@@ -673,6 +721,7 @@ namespace M17N.Core
     // 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<Tag, List<MDatabase>> kv in ndict)
        foreach (MDatabase mdb in kv.Value)
@@ -689,39 +738,91 @@ namespace M17N.Core
            if (mdb.DBStatus == MDBStatus.READY)
              {
                M17n.DebugPrint ("re-expanding: {0}\n", mdb);
-               register_files (DBDirs[dir_idx].Dirname, mdb.ListIndex,
-                               dir_idx, mdb.Info);
+               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 list_idx, int dir_idx,
-                                       MDatabaseInfo base_info)
+    private static void register_files (string dir, int dir_idx, MDatabase mdb)
     {
+      int list_idx = mdb.ListIndex;
       List<FileInfo> files = new List<FileInfo> ();
-      MGlob.FileList (ref files, dir, base_info.Filename);
+      MGlob.FileList (ref files, dir, mdb.Info.Filename);
       foreach (FileInfo fileinfo in files)
        {
-         MPlist plist = null;
-         using (FileStream stream = fileinfo.OpenRead ())
-           {
-             MStreamReader reader = new MStreamReader (stream);
-             plist = new MPlist (reader, 1);
-           }
-         if (plist != null && plist.IsPlist)
-           {
-             plist = plist.Plist;
-             Tag tag = new Tag (ref plist);
+         Tag tag;
+         MDatabaseInfo info;
 
-             if (! tag.HasWildcard && tag.Match (tag))
-               {
-                 MDatabaseInfo info = new MDatabaseInfo (plist);
-                 info.Merge (base_info);
-                 if (Path.IsPathRooted (base_info.Filename))
-                   info.Filename = fileinfo.FullName;
-                 else
-                   info.Filename = fileinfo.Name;
-                 register (list_idx, dir_idx, tag, 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);
            }
        }
     }
@@ -731,60 +832,66 @@ namespace M17N.Core
       M17n.DebugPrint ("expanding: {0}\n", this);
 
       if (DirIndex == 0)
-       register_files (null, ListIndex, DirIndex, Info);
+       register_files (null, DirIndex, this);
       else
        for (int i = 1; i < 4; i++)
          if (DBDirs[i].DirInfo != null)
-           register_files (DBDirs[i].DirInfo.FullName, ListIndex, i, Info);
+           register_files (DBDirs[i].DirInfo.FullName, i, this);
       DBStatus = MDBStatus.READY;
     }
 
     private static void maybe_expand_wildcard (Tag tag)
     {
       foreach (KeyValuePair<Tag, List<MDatabase>> kv in wdict)
-       if (kv.Key.Match (tag))
-         foreach (MDatabase mdb in kv.Value)
-           if (mdb.DBStatus == MDBStatus.NOT_READY)
-             mdb.expand_wildcard ();
-    }
-
-    private bool update_status ()
-    {
-      if (DBType == MDBType.UNKNOWN)
-       return true;
-      if (DBStatus == MDBStatus.READY
-         && FileInfo != null)
        {
-         FileInfo.Refresh ();
-         if (CheckedTime >= FileInfo.LastWriteTime)
-           return true;
-
-
+         M17n.DebugPrint ("expand check: {0}\n", kv.Key);
+         if (kv.Key.Match (tag))
            {
-             FileInfo[] files
-               = DBDirs[DirIndex].DirInfo.GetFiles (Info.Filename);
-             if (files.Count > 0)
-               FileInfo = files[0];
+             foreach (MDatabase mdb in kv.Value)
+               {
+                 if (mdb.DBStatus == MDBStatus.NOT_READY)
+                   mdb.expand_wildcard ();
+               }
            }
-
        }
-
-      if (DBDirs[DirIndex].DirInfo == null)
-       return false;
-      FileInfo[] files = DBDirs[DirIndex].DirInfo.GetFiles (Info.Filename);
-      if (files.Length == 0)
-       return false;
-      FileInfo = files[0];
-      return true;
     }
 
-    private bool find_file ()
+    // Update the status.  Return true iff the database file is
+    // readable but changed.
+
+    private bool update_status ()
     {
-      if (DirIndex == 0)
+      if (DBType == MDBType.UNKNOWN)
+       return true;
+      update_all (false);
+      if (DBStatus == MDBStatus.INVALID)
+       return false;
+      if (DBStatus != MDBStatus.NOT_READY)
        {
-         
-       }
-      return true;
+         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)
@@ -807,14 +914,18 @@ namespace M17N.Core
                break;
            }
        }
-      if (! update_all () && mdb != null)
+      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++)
-       if ((mdb = mdbs[i]).check_file ())
-         return mdb;
+       {
+         mdb = mdbs[i];
+         mdb.update_status ();
+         if (mdb.DBStatus == MDBStatus.READY)
+           return mdb;
+       }
       return null;
     }
 
@@ -822,7 +933,7 @@ namespace M17N.Core
     {
       List<MDatabase> list = new List<MDatabase> ();
 
-      update_all ();
+      update_all (false);
       maybe_expand_wildcard (tag);
 
       if (tag.HasWildcard)
@@ -830,44 +941,266 @@ namespace M17N.Core
          foreach (KeyValuePair<Tag, List<MDatabase>> kv in ndict)
            if (kv.Key.Match (tag))
              foreach (MDatabase mdb in kv.Value)
-               if (mdb.check_file ())
-                 {
-                   list.Add (mdb);
-                   break;
-                 }
+               {
+                 mdb.update_status ();
+                 if (mdb.DBStatus == MDBStatus.READY)
+                   {
+                     list.Add (mdb);
+                     break;
+                   }
+               }
        }
       else
        {
          List<MDatabase> mdbs;
          if (ndict.TryGetValue (tag, out mdbs))
            foreach (MDatabase mdb in mdbs)
-             if (mdb.check_file ())
-               {
-                 list.Add (mdb);
-                 break;
-               }
+             {
+               mdb.update_status ();
+               if (mdb.DBStatus == MDBStatus.READY)
+                 {
+                   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 (name_table);
+         try {
+           XmlTextReader reader
+             = new XmlTextReader (FileInfo.FullName, name_table);
+           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;
+      if (Info.Format == Mxml)
+       {
+         XmlDocument doc = new XmlDocument (name_table);
+         XmlTextReader reader = new XmlTextReader (stream, name_table);
+
+         reader.WhitespaceHandling = WhitespaceHandling.None;
+         try {
+           reader.Read ();
+           while (reader.NodeType != XmlNodeType.Element)
+             reader.Read ();
+           doc.LoadXml ("<" + reader.Name + "></" + 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;
+    }
+
+    public object Load (MSymbol stop)
+    {
+      FileStream stream = get_stream ();
+
+      if (stream == null)
        return null;
-      return load (key, stop);
+      if (Info.Format == Mxml)
+       {
+         XmlDocument doc = new XmlDocument (name_table);
+         XmlTextReader reader = new XmlTextReader (stream, name_table);
+
+         reader.WhitespaceHandling = WhitespaceHandling.None;
+         try {
+           reader.Read ();
+           while (reader.NodeType != XmlNodeType.Element)
+             reader.Read ();
+           doc.LoadXml ("<" + reader.Name + "></" + 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;
     }
 
-    private object load (MSymbol key, MSymbol stop)
+    public XmlNode Load (string id, params string[] nodes)
     {
-      return null;
+      FileStream stream = get_stream ();
+      if (stream == null)
+       return null;
+      if (Info.Format != Mxml)
+       throw new Exception ("Not an XML format");
+
+      XmlDocument doc = new XmlDocument (name_table);
+      XmlTextReader reader = new XmlTextReader (stream, name_table);
+      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;
     }
 
+    /// <summary>Return a list of currently available database
+    /// directory names</summary>.
+    public static string[] DirectoryList ()
+    {
+      List<string> dirs = new List<string> ();
+
+      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<MDatabase>
     public int CompareTo (MDatabase other)
     {
@@ -876,4 +1209,4 @@ namespace M17N.Core
              : ListIndex - other.ListIndex);
     }
   }
-}
\ No newline at end of file
+}