using System;
+using System.Collections;
using System.Collections.Generic;
using System.IO;
+using System.Xml;
+
using M17N;
using M17N.Core;
namespace M17N.Core
{
- public delegate object MDatabaseLoader (MDatabaseTag tag,
- object extra_info);
-
- public struct MDatabaseTag
+ internal class MGlob
{
- public MSymbol Tag0, Tag1, Tag2, Tag3;
-
- public MDatabaseTag (MSymbol tag0)
- {
- Tag0 = tag0; Tag1 = Tag2 = Tag3 = MSymbol.nil;
- }
+ static readonly char sep = Path.DirectorySeparatorChar;
- public MDatabaseTag (MSymbol tag0, MSymbol tag1)
- {
- Tag0 = tag0; Tag1 = tag1; Tag2 = Tag3 = MSymbol.nil;
- }
+ public static void FileList (ref List<FileInfo> files,
+ string dir, string pattern)
+ {
+ int len = pattern.Length;
- public MDatabaseTag (MSymbol tag0, MSymbol tag1, MSymbol tag2)
- {
- Tag0 = tag0; Tag1 = tag1; Tag2 = tag2; Tag3 = MSymbol.nil;
- }
+ if (Path.IsPathRooted (pattern))
+ {
+ int headsep = 0;
+ int i;
- public MDatabaseTag (MSymbol tag0, MSymbol tag1,
- MSymbol tag2, MSymbol tag3)
- {
- Tag0 = tag0; Tag1 = tag1; Tag2 = tag2; Tag3 = tag3;
- }
- }
+ 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);
+ }
- public class MDatabase
- {
- private class MDatabaseDir
+ private static void list (ref List<FileInfo> files,
+ DirectoryInfo dirinfo, string pattern)
{
- private const string ListFileName = "mdb.dir";
-
- public string Dirname;
- public DirectoryInfo DirInfo;
- public DateTime LastScanned;
- public FileInfo ListInfo;
-
- private static void GetInfo (string dirname, out DirectoryInfo dirinfo,
- out FileInfo listinfo)
- {
- if (Directory.Exists (dirname))
- try { dirinfo = new DirectoryInfo (dirname);
- try { listinfo = dirinfo.GetFiles (ListFileName)[0];
- } catch { listinfo = null; }
- } catch { dirinfo = null; listinfo = null; }
+ 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
{
- dirinfo = null;
- listinfo = null;
+ 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);
+ }
+ }
- public MDatabaseDir (string dirname)
- {
- Dirname = dirname;
- GetInfo (dirname, out DirInfo, out ListInfo);
- }
+ internal class MDatabaseDir
+ {
+ private const string ListFileName = "mdb.dir";
- public bool StatusChanged {
- get {
- bool exists = Directory.Exists (Dirname);
+ public string Dirname;
+ public DirectoryInfo DirInfo;
+ public DateTime DirChangeTime;
+ public FileInfo ListInfo;
+ public DateTime ListChangeTime;
- if (DirInfo != null)
+ public MDatabaseDir (string dirname)
+ {
+ Dirname = dirname;
+ DirChangeTime = ListChangeTime = DateTime.Now;
+ }
+
+ public void Refresh ()
+ {
+ if (DirInfo != null)
+ {
+ if (Dirname != null && Directory.Exists (Dirname))
{
- if (! exists)
- {
- DirInfo = null;
- ListInfo = null;
- LastScanned = new DateTime (0);
- }
- if (LastScanned.Year == 0)
- return true;
DirInfo.Refresh ();
- if (ListInfo != null)
- ListInfo.Refresh ();
- return (LastScanned < DirInfo.LastWriteTime
- || LastScanned < ListInfo.LastWriteTime);
+ 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
{
- if (exists)
+ try {
+ ListInfo = DirInfo.GetFiles (ListFileName)[0];
+ ListChangeTime = DateTime.Now;
+ } catch {
+ }
+ }
+ }
+ }
+ }
+
+ public partial class MDatabase : IComparable<MDatabase>
+ {
+ /// Identifier of a MDatabase.
+ public struct Tag : IEquatable<Tag>
+ {
+ 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)
{
- DirInfo = new DirectoryInfo (Dirname);
- try {
- ListInfo = DirInfo.GetFiles (ListFileName)[0];
- } catch {
- ListInfo = null;
- }
- return true;
+ Tags[i] = plist.Symbol;
+ plist = plist.Next;
}
- return false;
+ 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 void UpdateStatus ()
+ public override int GetHashCode ()
{
- if (DirInfo != null)
- LastScanned = DateTime.UtcNow;
+ return (Tags[0].GetHashCode () ^ Tags[1].GetHashCode ()
+ ^ Tags[2].GetHashCode () ^ Tags[3].GetHashCode ());
}
- public FileInfo[] Scan (string filename)
+ public override string ToString ()
{
- if (DirInfo == null)
- return null;
- DirInfo.Refresh ();
- return DirInfo.GetFiles (filename);
+ 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;
+ }
}
}
- internal class MDatabaseInfo {
- internal DirectoryInfo Dir;
+ public delegate object Loader (Tag tag, object extra_info);
+
+ internal class MDatabaseInfo
+ {
+ // These come from the declartion plist of the database.
internal string Description;
- internal MText Filename;
- internal FileInfo FileInfo;
+ internal string Filename;
internal FileInfo Validater;
internal int Version;
internal MSymbol Format;
internal MSymbol Schema;
- internal MText SchemaFile;
- internal DateTime ModifiedTime;
+ internal string SchemaFile;
internal MPlist Props;
+
+ public MDatabaseInfo ()
+ {
+ 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)
+ {
+ 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 = ("#<Info " + Format + " \"" + Filename + "\"");
+ if (Schema != MSymbol.nil)
+ str += " " + Schema;
+ return str + ">";
+ }
}
- private static Dictionary<MDatabaseTag, MDatabase> DBDict
- = new Dictionary<MDatabaseTag, MDatabase> ();
+ // Dictionaries for normal databases.
+ private static Dictionary<MDatabase.Tag, List<MDatabase>> ndict
+ = new Dictionary<MDatabase.Tag, List<MDatabase>> ();
+
+ // Dictionaries for databases of DBType WILDCARD
+ private static Dictionary<MDatabase.Tag, List<MDatabase>> wdict
+ = new Dictionary<MDatabase.Tag, List<MDatabase>> ();
+
+ private static MDatabaseDir[] DBDirs = new MDatabaseDir[4];
- private static MDatabaseDir[] DBDirs = new MDatabaseDir[3];
+ private static DateTime LastUpdateTime = new DateTime (0);
- private const string SystemDirectory = "/usr/share/m17n";
- private readonly MSymbol Mversion = new MSymbol ("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 TimeSpan CheckInterval = new TimeSpan (50000000);
/// Type of database
private enum MDBType
{
- /// The database was defined automatically from mdb.dir
- /// file(s) with no wildcard tag.
+ /// The database was defined automatically from one of mdb.dir
+ /// files with no wildcard tag.
AUTO,
- /// The database was defined automatically from mdb.dir
- /// file(s) with a wildcard tag to define multiple databases
+ /// 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
- /// without a special loader.
+ /// to use the normal loader.
EXPLICIT,
/// The database was defined explicitely by MDatabase.Define
- /// with a special loader.
+ /// 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 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 readonly MDatabaseTag Tag;
- private MDatabaseLoader Loader;
+ public Tag tag;
+ public NameTable name_table = new NameTable ();
+ 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 ()
+ {
+ 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);
+ }
- public static string ApplicationDirectory;
+ Console.WriteLine ("[WDICT]");
+ foreach (KeyValuePair<Tag, List<MDatabase>> kv in wdict)
+ foreach (MDatabase mdb in kv.Value)
+ Console.WriteLine (mdb);
- private MDatabase (MDatabaseTag tag, MDatabaseLoader loader,
- object extra_info)
+ Console.WriteLine ("[NDICT]");
+ foreach (KeyValuePair<Tag, List<MDatabase>> kv in ndict)
+ foreach (MDatabase mdb in kv.Value)
+ Console.WriteLine (mdb);
+ }
+
+ private static void register (MDatabase mdb)
+ {
+ Dictionary<MDatabase.Tag, List<MDatabase>> dict
+ = mdb.DBType == MDBType.WILDCARD ? wdict : ndict;
+ List<MDatabase> 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<MDatabase> (1);
+ mdbs.Add (mdb);
+ dict.Add (mdb.tag, mdbs);
+ }
+ }
+
+ 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 (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<MDatabase> (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)
{
- Tag = tag;
- Loader = loader;
+ this.tag = tag;
+ this.loader = loader;
+ DBType = MDBType.UNKNOWN;
+ DBStatus = MDBStatus.READY;
+ DirIndex = 0;
+ ListIndex = 0;
ExtraInfo = extra_info;
+ register (this);
}
- private MDatabase (MDatabaseTag tag, string filename)
+ public MDatabase (Tag tag, string filename)
{
- Tag = tag;
+ 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 = new MText (filename);
+ Info.Filename = filename;
+ register (this);
}
- private MDatabase (MPlist plist)
+ private MDatabase (int list_idx, int dir_idx, Tag tag, MDatabaseInfo info)
{
- MSymbol[] tags = new MSymbol[4];
- int i;
+ 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 = ("#<MDataBase (" + ListIndex + "," + DirIndex + ") "
+ + tag + " " + DBType + " " + DBStatus);
+
+ if (DBType != MDBType.EXPLICIT && DBType != MDBType.UNKNOWN)
+ str += " " + Info;
+ return 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<Tag, List<MDatabase>> 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<Tag, List<MDatabase>> 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;
- for (i = 0; plist.IsSymbol; i++, plist = plist.Next)
- tags[i] = plist.Symbol;
- while (i < 4)
- tags[i++] = MSymbol.nil;
- Tag = new MDatabaseTag (tags[0], tags[1], tags[2], tags[3]);
- if (plist.IsMText)
+ MPlist plist = null;
+ using (FileStream stream = File.OpenRead (dblist.FullName))
{
- Info.Filename = plist.Text;
- plist = plist.Next;
+ plist = new MPlist (stream);
}
- else if (plist.IsPlist)
+ 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<Tag, List<MDatabase>> 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<Tag, List<MDatabase>> 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 ())
{
- MPlist p = plist.Plist;
+ 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;
+ }
- if (p.IsMText)
- Info.Filename = plist.Text;
- p = p.Next;
- if (! p.IsEmpty)
+ 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, 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))
{
- 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;
- }
+ 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);
}
- plist = plist.Next;
}
- DBStatus = MDBStatus.OUTDATED;;
- Info.Version = 0;
- Info.Props = new MPlist ();
- foreach (MPlist pl in plist)
+ }
+
+ 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<Tag, List<MDatabase>> kv in wdict)
{
- if (pl.IsPlist)
+ M17n.DebugPrint ("expand check: {0}\n", kv.Key);
+ if (kv.Key.Match (tag))
{
- MPlist p = pl.Plist;
-
- if (p.IsSymbol && p.Symbol == Mversion)
+ foreach (MDatabase mdb in kv.Value)
{
- Info.Version = parse_version (p.Next);
- if (M17n.Version < Info.Version)
- DBStatus = MDBStatus.DISABLED;
+ if (mdb.DBStatus == MDBStatus.NOT_READY)
+ mdb.expand_wildcard ();
}
}
- Info.Props.Put (pl.Key, pl.Val);
}
}
- private static int parse_version (MPlist plist)
- {
- string[] str;
- int major, minor, release;
+ // Update the status. Return true iff the database file is
+ // readable but changed.
- 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);
+ 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 Define (MDatabaseTag tag, MDatabaseLoader loader,
- object extra_info)
+ public static MDatabase Find (Tag tag)
{
- MDatabase db = MDatabase.Find (tag);
+ List<MDatabase> mdbs;
+ MDatabase mdb = null;
+
+ if (tag.HasWildcard)
+ throw new ArgumentException ("Wildcard not allowed: " + tag);
- if (db != null)
+ 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++)
{
- db.Loader = loader;
- db.ExtraInfo = extra_info;
- db.DBType = MDBType.EXPLICIT;
- db.DBStatus = MDBStatus.OUTDATED;
- db.Info = null;
- return db;
+ mdb = mdbs[i];
+ mdb.update_status ();
+ if (mdb.DBStatus == MDBStatus.READY)
+ return mdb;
}
- return new MDatabase (tag, loader, extra_info);
+ return null;
}
- public static MDatabase Define (MDatabaseTag tag, string filename)
+ public static List<MDatabase> List (Tag tag)
{
- MDatabase db = MDatabase.Find (tag);
+ List<MDatabase> list = new List<MDatabase> ();
- if (db != null)
- {
- db.Loader = null;
- db.DBType = MDBType.EXPLICIT;
- db.DBStatus = MDBStatus.OUTDATED;
- db.Info = new MDatabaseInfo ();
- db.Info.Filename = new MText (filename);
+ update_all (false);
+ maybe_expand_wildcard (tag);
- return db;
+ if (tag.HasWildcard)
+ {
+ foreach (KeyValuePair<Tag, List<MDatabase>> 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<MDatabase> 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 new MDatabase (tag, filename);
+ return list;
}
- static MDatabase ()
+ private FileStream get_stream ()
{
- string share_dir = (Environment.GetFolderPath
- (Environment.SpecialFolder.CommonApplicationData));
- string usr_dir = (Environment.GetFolderPath
- (Environment.SpecialFolder.ApplicationData));
+ 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 {
- DBDirs[0] = new MDatabaseDir (Path.Combine (usr_dir, ".m17n.d"));
- } catch (ArgumentException) {
- DBDirs[0] = new MDatabaseDir (Path.Combine (usr_dir, "_m17n_d"));
+ stream = FileInfo.OpenRead ();
+ } catch {
+ LastLoadStatus = LoadStatus.NotReadable;
}
- DBDirs[1] = null;
- DBDirs[2] = new MDatabaseDir (Path.Combine (share_dir, "m17n"));
+ return stream;
}
- private static void Update ()
+ public object Load ()
{
+ 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 static MDatabase Find (MDatabaseTag tag)
+ public object Load (MSymbol key, MSymbol stop)
{
- MDatabase db;
+ 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;
+ }
- return (DBDict.TryGetValue (tag, out db) ? db : null);
+ 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 ()
+ public object Load (MSymbol stop)
{
- return (Loader != null ? Loader (Tag, ExtraInfo)
- : load (MSymbol.nil, MSymbol.nil));
+ 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 = 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 object Load (MSymbol key, MSymbol stop)
+ public XmlNode Load (string id, params string[] nodes)
{
- if (Loader != null)
+ FileStream stream = get_stream ();
+ if (stream == null)
return null;
- return load (key, stop);
+ 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;
}
- private object load (MSymbol key, MSymbol stop)
+ /// <summary>Return a list of currently available database
+ /// directory names</summary>.
+ public static string[] DirectoryList ()
{
- LoadedTime = DateTime.UtcNow;
+ 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 ();
+ }
- return null;
+ 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 ();
+ }
-}
\ No newline at end of file
+ // For IComparable<MDatabase>
+ public int CompareTo (MDatabase other)
+ {
+ return (ListIndex == other.ListIndex
+ ? DirIndex - other.DirIndex
+ : ListIndex - other.ListIndex);
+ }
+ }
+}