X-Git-Url: http://git.chise.org/gitweb/?a=blobdiff_plain;f=MDatabase.cs;h=01d575520511262f884c80a0e13bd34059e61462;hb=166a1a78da9a3f710f35813ce870190ba671ba6b;hp=6c601de660392c2250b56cad19ed4d4d6b6c7462;hpb=a31a15176c89540d23fc870e6bd67c5af23bbd5f;p=m17n%2Fm17n-lib-cs.git diff --git a/MDatabase.cs b/MDatabase.cs index 6c601de..01d5755 100644 --- a/MDatabase.cs +++ b/MDatabase.cs @@ -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 + public partial class MDatabase : IComparable { /// Identifier of a MDatabase. public struct Tag : IEquatable @@ -233,7 +240,7 @@ namespace M17N.Core } } - public delegate object Loader (Tag tag, object extra_info); + public delegate MPlist Loader (Tag tag, object extra_info); internal class MDatabaseInfo { @@ -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,7 +412,7 @@ 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, }; @@ -417,10 +428,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 +464,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 +496,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) @@ -542,10 +570,6 @@ namespace M17N.Core mdb = mdbs[i]; if (mdb.ListIndex == list_idx && mdb.DirIndex >= dir_idx) { - if (mdb.DBStatus == MDBStatus.INVALID) - M17n.DebugPrint ("registering: {0}\n", mdb); - else - M17n.DebugPrint ("updating: {0}\n", mdb); mdb.DirIndex = dir_idx; if (dict == wdict) { @@ -563,6 +587,10 @@ namespace M17N.Core 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; } } @@ -616,14 +644,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 () { @@ -707,38 +737,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 files = new List (); - 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 ()) - { - plist = new MPlist (stream, 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); } } } @@ -748,11 +831,11 @@ 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; } @@ -772,20 +855,29 @@ namespace M17N.Core } } + // 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.READY) + if (DBStatus != MDBStatus.NOT_READY) { - if (DirIndex > 0 - || File.Exists (FileInfo.FullName)) - return true; - DBStatus = MDBStatus.INVALID; - return false; - } + 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) { @@ -794,8 +886,9 @@ namespace M17N.Core { FileInfo = new FileInfo (filename); DirIndex = i; + DBStatus = MDBStatus.READY; return true; - } + } } return false; } @@ -820,14 +913,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]).update_status ()) - return mdb; + { + mdb = mdbs[i]; + mdb.update_status (); + if (mdb.DBStatus == MDBStatus.READY) + return mdb; + } return null; } @@ -835,7 +932,7 @@ namespace M17N.Core { List list = new List (); - update_all (); + update_all (false); maybe_expand_wildcard (tag); if (tag.HasWildcard) @@ -843,45 +940,264 @@ namespace M17N.Core foreach (KeyValuePair> kv in ndict) if (kv.Key.Match (tag)) foreach (MDatabase mdb in kv.Value) - if (mdb.update_status ()) - { - list.Add (mdb); - break; - } + { + 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) - if (mdb.update_status ()) - { - list.Add (mdb); - break; - } + { + mdb.update_status (); + if (mdb.DBStatus == MDBStatus.READY) + { + list.Add (mdb); + break; + } + } } return list; } - public object Load () + private FileStream get_stream () { - return (loader != null ? loader (tag, ExtraInfo) - : load (MSymbol.nil, MSymbol.nil)); + 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 (MSymbol key, MSymbol stop) + 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; - return load (key, stop); + MPlist plist = null; + try { + plist = new MPlist (stream); + LastLoaded = DateTime.Now; + } catch { + LastLoadStatus = LoadStatus.InvalidContents; + } finally { + stream.Dispose (); + } + return plist; } - private object load (MSymbol key, MSymbol stop) + public MPlist Load (MSymbol key, MSymbol stop) { - return null; + 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; } - // Return a list of currently available database directories. + 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 (); @@ -892,6 +1208,19 @@ namespace M17N.Core 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) { @@ -900,4 +1229,4 @@ namespace M17N.Core : ListIndex - other.ListIndex); } } -} \ No newline at end of file +}