2 using System.Collections;
3 using System.Collections.Generic;
12 static readonly char sep = Path.DirectorySeparatorChar;
14 public static void FileList (ref List<FileInfo> files,
15 string dir, string pattern)
17 int len = pattern.Length;
19 if (Path.IsPathRooted (pattern))
24 for (i = 1; i < len && (pattern[i] != '*' && pattern[i] != '?'); i++)
25 if (pattern[i] == sep)
29 if (File.Exists (pattern))
30 files.Add (new FileInfo (pattern));
33 dir = pattern.Substring (0, headsep);
34 pattern = pattern.Substring (headsep + 1);
39 dir = Directory.GetCurrentDirectory ();
41 if (Directory.Exists (dir))
42 list (ref files, new DirectoryInfo (dir), pattern);
45 private static void list (ref List<FileInfo> files,
46 DirectoryInfo dirinfo, string pattern)
48 int len = pattern.Length;
51 for (i = 0; i < len && pattern[i] != sep; i++);
55 FileInfo[] listing = dirinfo.GetFiles (pattern);
56 foreach (FileInfo elt in listing)
61 string tail = pattern.Substring (i + 1);
62 pattern = pattern.Substring (0, i);
63 DirectoryInfo[] listing = dirinfo.GetDirectories (pattern);
64 foreach (DirectoryInfo elt in listing)
65 list (ref files, elt, tail);
72 internal class MDatabaseDir
74 private const string ListFileName = "mdb.dir";
76 public string Dirname;
77 public DirectoryInfo DirInfo;
78 public DateTime DirChangeTime;
79 public FileInfo ListInfo;
80 public DateTime ListChangeTime;
82 public MDatabaseDir (string dirname)
85 DirChangeTime = ListChangeTime = DateTime.Now;
88 public void Refresh ()
92 if (Dirname != null && Directory.Exists (Dirname))
95 if (DirChangeTime < DirInfo.LastWriteTime)
96 DirChangeTime = DirInfo.LastWriteTime;
101 DirChangeTime = DateTime.Now;
106 if (Dirname != null && Directory.Exists (Dirname))
108 DirInfo = new DirectoryInfo (Dirname);
109 DirChangeTime = DateTime.Now;
114 if (ListInfo != null)
117 ListChangeTime = DateTime.Now;
122 if (ListInfo != null)
125 if (ListChangeTime < ListInfo.LastWriteTime)
126 ListChangeTime = ListInfo.LastWriteTime;
131 ListInfo = DirInfo.GetFiles (ListFileName)[0];
132 ListChangeTime = DateTime.Now;
140 public partial class MDatabase : IComparable<MDatabase>
142 /// Identifier of a MDatabase.
143 public struct Tag : IEquatable<Tag>
145 private MSymbol[] Tags;
147 public Tag (MSymbol tag0)
149 Tags = new MSymbol[4];
150 Tags[0] = tag0; Tags[1] = Tags[2] = Tags[3] = MSymbol.nil;
153 public Tag (MSymbol tag0, MSymbol tag1)
155 Tags = new MSymbol[4];
156 Tags[0] = tag0; Tags[1] = tag1; Tags[2] = Tags[3] = MSymbol.nil;
159 public Tag (MSymbol tag0, MSymbol tag1, MSymbol tag2)
161 Tags = new MSymbol[4];
162 Tags[0] = tag0; Tags[1] = tag1; Tags[2] = tag2; Tags[3] = MSymbol.nil;
165 public Tag (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
167 Tags = new MSymbol[4];
168 Tags[0] = tag0; Tags[1] = tag1; Tags[2] = tag2; Tags[3] = tag3;
171 public Tag (ref MPlist plist)
173 Tags = new MSymbol[4];
175 for (int i = 0; i < 4; i++)
179 Tags[i] = plist.Symbol;
183 Tags[i] = MSymbol.nil;
187 public bool Equals (Tag tag)
189 for (int i = 0; i < 4; i++)
190 if (tag[i] != Tags[i])
195 public override int GetHashCode ()
197 return (Tags[0].GetHashCode () ^ Tags[1].GetHashCode ()
198 ^ Tags[2].GetHashCode () ^ Tags[3].GetHashCode ());
201 public override string ToString ()
204 + Tags[0] + "," + Tags[1] + "," + Tags[2] + "," + Tags[3]
208 public MSymbol this[int i]
210 set { Tags[i] = value; }
211 get { return Tags[i]; }
214 public bool Match (Tag tag)
216 for (int i = 0; i < 4; i++)
218 if (tag[i] == Mwildcard || Tags[i] == Mwildcard)
220 if (tag[i] != Tags[i])
226 public bool HasWildcard {
228 for (int i = 0; i < 4; i++)
229 if (Tags[i] == Mwildcard)
236 public delegate object Loader (Tag tag, object extra_info);
238 internal class MDatabaseInfo
240 // These come from the declartion plist of the database.
241 internal string Description;
242 internal string Filename;
243 internal FileInfo Validater;
244 internal int Version;
245 internal MSymbol Format;
246 internal MSymbol Schema;
247 internal string SchemaFile;
248 internal MPlist Props;
250 public MDatabaseInfo ()
252 Format = Schema = MSymbol.nil;
255 private static int parse_version (MPlist plist)
258 int major, minor, release;
262 str = plist.Text.ToString ().Split ('.');
265 try { major = int.Parse (str[0]); } catch { return 0xFFFFFF; }
266 try { minor = int.Parse (str[1]); } catch { return 0xFFFFFF; }
267 try { release = int.Parse (str[2]); } catch { return 0xFFFFFF; }
268 return ((major << 16) | (minor << 8) | release);
271 public MDatabaseInfo (MPlist plist)
273 Format = MSymbol.plist;
276 Filename = plist.Text.ToString ();
279 else if (plist.IsPlist)
281 MPlist p = plist.Plist;
284 Filename = p.Text.ToString ();
297 SchemaFile = p.Text.ToString ();
304 Props = new MPlist ();
305 foreach (MPlist pl in plist)
311 if (p.IsSymbol && p.Symbol == Mversion)
312 Version = parse_version (p.Next);
314 Props.Put (pl.Key, pl.Val);
316 else if (pl.IsSymbol)
318 MPlist p = new MPlist ();
319 p.Add (MSymbol.symbol, pl.Symbol);
320 p.Add (MSymbol.symbol, MSymbol.t);
321 Props.Put (MSymbol.plist, p);
326 public void Merge (MDatabaseInfo src)
328 if (Validater == null)
329 Validater = src.Validater;
331 Version = src.Version;
332 if (Format == MSymbol.nil)
334 if (Schema == MSymbol.nil)
336 if (SchemaFile == null)
337 SchemaFile = src.SchemaFile;
338 foreach (MPlist p in src.Props)
339 if (Props.Assq (p.Plist.Symbol) == null)
340 Props.Push (p.Key, p.Val);
343 public override string ToString ()
345 string str = ("#<Info " + Format + " \"" + Filename + "\"");
346 if (Schema != MSymbol.nil)
352 // Dictionaries for normal databases.
353 private static Dictionary<MDatabase.Tag, List<MDatabase>> ndict
354 = new Dictionary<MDatabase.Tag, List<MDatabase>> ();
356 // Dictionaries for databases of DBType WILDCARD
357 private static Dictionary<MDatabase.Tag, List<MDatabase>> wdict
358 = new Dictionary<MDatabase.Tag, List<MDatabase>> ();
360 private static MDatabaseDir[] DBDirs = new MDatabaseDir[4];
362 private static DateTime LastUpdateTime = new DateTime (0);
364 private static readonly MSymbol Mversion = MSymbol.Of ("version");
365 private static readonly MSymbol Mwildcard = MSymbol.Of ("*");
366 private static readonly MSymbol Mchar_table = MSymbol.Of ("char-table");
367 private static readonly MSymbol Mcharset = MSymbol.Of ("charset");
372 /// The database was defined automatically from one of mdb.dir
373 /// files with no wildcard tag.
375 /// The database was defined automatically from one of mdb.dir
376 /// files with a wildcard tag to define multiple databases
377 /// of the same kind.
379 /// The database was defined explicitely by MDatabase.Define
380 /// to use the normal loader.
382 /// The database was defined explicitely by MDatabase.Define
383 /// to use a special loader.
385 /// The database is for defining multiple databases of the
386 /// same kind with a wildcard tag.
390 /// Status of database
391 private enum MDBStatus
393 // The database file has not yet been decided, or is not yet
394 // expanded if DBType is WILDCARD.
396 // The database file was decided, or has been expanded if
397 // DBType is WILDCARD.
399 // The database is disabled. It means that the database file
400 // is not readable, the version is not supported by the
401 // current system, or the validation was failed.
403 // The database is deleted by the modificaiton of mdb.dir, or
404 // is overwritten by a new explicit definition..
409 private Loader loader;
410 private object ExtraInfo;
411 // Directory of the database file.
412 // -1:unknown, 0:absolute, 1:DBDirs[1], 2:DBDirs[2], 3:DBDirs[3]
413 private int DirIndex;
414 // Directory of the mdb.dir defining the database file.
415 // 0: EXPLICIT or UNKNOWN, 1:DBDirs[1], 2:DBDirs[2], 3:DBDirs[3]
416 private int ListIndex;
417 private MDBType DBType;
418 private MDBStatus DBStatus;
419 internal MDatabaseInfo Info;
420 // File in which the database contents is stored.
421 internal FileInfo FileInfo;
422 // When the database file is checked (or validated).
423 internal DateTime CheckedTime;
427 string share_dir = (Environment.GetFolderPath
428 (Environment.SpecialFolder.CommonApplicationData));
429 string usr_dir = (Environment.GetFolderPath
430 (Environment.SpecialFolder.ApplicationData));
433 string dir = Environment.GetEnvironmentVariable ("M17NDIR");
434 DBDirs[1] = new MDatabaseDir (dir);
437 DBDirs[1] = new MDatabaseDir (Path.Combine (usr_dir, ".m17n.d"));
438 } catch (ArgumentException) {
439 DBDirs[1] = new MDatabaseDir (Path.Combine (usr_dir, "_m17n_d"));
442 DBDirs[2] = new MDatabaseDir (null);
443 DBDirs[3] = new MDatabaseDir (Path.Combine (share_dir, "m17n"));
447 public static string ApplicationDir
449 get { return (DBDirs[1].Dirname); }
450 set { DBDirs[2] = new MDatabaseDir (value); update_all (); }
453 private static bool update_all ()
455 bool updated = false;
457 for (int i = 1; i < 4; i++)
458 if (DBDirs[i].Dirname != null)
460 DBDirs[i].Refresh ();
461 if (LastUpdateTime < DBDirs[i].ListChangeTime)
466 if (LastUpdateTime < DBDirs[i].DirChangeTime)
472 LastUpdateTime = DateTime.Now;
476 public static void Dump ()
479 Console.WriteLine ("[DBDirs]");
480 for (int i = 1; i < 4; i++)
481 if (DBDirs[i].Dirname != null)
483 if (DBDirs[i].DirInfo != null)
485 Console.Write ("{0}:{1}", i, DBDirs[i].DirInfo.FullName);
486 if (DBDirs[i].ListInfo != null)
487 Console.WriteLine (" {0}", DBDirs[i].ListInfo);
489 Console.WriteLine (" .. no mdb.dir");
492 Console.WriteLine ("{0}:{1} .. not exist", i, DBDirs[i].Dirname);
495 Console.WriteLine ("[WDICT]");
496 foreach (KeyValuePair<Tag, List<MDatabase>> kv in wdict)
497 foreach (MDatabase mdb in kv.Value)
498 Console.WriteLine (mdb);
500 Console.WriteLine ("[NDICT]");
501 foreach (KeyValuePair<Tag, List<MDatabase>> kv in ndict)
502 foreach (MDatabase mdb in kv.Value)
503 Console.WriteLine (mdb);
506 private static void register (MDatabase mdb)
508 Dictionary<MDatabase.Tag, List<MDatabase>> dict
509 = mdb.DBType == MDBType.WILDCARD ? wdict : ndict;
510 List<MDatabase> mdbs;
512 if (dict.TryGetValue (mdb.tag, out mdbs))
514 for (int i = 0; i < mdbs.Count; i++)
515 if (mdbs[i].ListIndex == mdb.ListIndex)
517 mdbs[i].DBStatus = MDBStatus.INVALID;
525 mdbs = new List<MDatabase> (1);
527 dict.Add (mdb.tag, mdbs);
531 private static void register (int list_idx, int dir_idx,
532 Tag tag, MDatabaseInfo info)
534 Dictionary<MDatabase.Tag, List<MDatabase>> dict
535 = tag.HasWildcard ? wdict : ndict;
536 List<MDatabase> mdbs;
539 if (dict.TryGetValue (tag, out mdbs))
540 for (int i = 0; i < mdbs.Count; i++)
543 if (mdb.ListIndex == list_idx && mdb.DirIndex >= dir_idx)
545 if (mdb.DBStatus == MDBStatus.INVALID)
546 M17n.DebugPrint ("registering: {0}\n", mdb);
548 M17n.DebugPrint ("updating: {0}\n", mdb);
549 mdb.DirIndex = dir_idx;
552 mdb.DBType = MDBType.WILDCARD;
553 mdb.DBStatus = MDBStatus.NOT_READY;
555 else if (dir_idx == -1)
557 mdb.DBType = MDBType.AUTO;
558 mdb.DBStatus = MDBStatus.NOT_READY;
562 mdb.DBType = MDBType.MULTIPLE;
563 mdb.DBStatus = MDBStatus.READY;
571 mdbs = new List<MDatabase> (1);
572 dict.Add (tag, mdbs);
574 mdb = new MDatabase (list_idx, dir_idx, tag, info);
575 M17n.DebugPrint ("registering: {0}\n", mdb);
579 public MDatabase (Tag tag, Loader loader, object extra_info)
582 this.loader = loader;
583 DBType = MDBType.UNKNOWN;
584 DBStatus = MDBStatus.READY;
587 ExtraInfo = extra_info;
591 public MDatabase (Tag tag, string filename)
594 DBType = MDBType.EXPLICIT;
595 if (Path.IsPathRooted (filename))
598 if (File.Exists (filename))
599 DBStatus = MDBStatus.READY;
601 DBStatus = MDBStatus.INVALID;
606 DBStatus = MDBStatus.NOT_READY;
609 Info = new MDatabaseInfo ();
610 Info.Filename = filename;
614 private MDatabase (int list_idx, int dir_idx, Tag tag, MDatabaseInfo info)
618 DBType = this.tag.HasWildcard ? MDBType.WILDCARD : MDBType.AUTO;
619 if (tag[0] == Mchar_table || tag[0] == Mcharset)
620 Info.Format = tag[0];
621 ListIndex = list_idx;
623 if (Path.IsPathRooted (Info.Filename))
624 DBStatus = MDBStatus.READY;
626 DBStatus = MDBStatus.NOT_READY;
627 if (Info.Format == Mchar_table)
628 MCharProp.Define (tag[2], this);
631 public override String ToString () {
632 string str = ("#<MDataBase (" + ListIndex + "," + DirIndex + ") "
633 + tag + " " + DBType + " " + DBStatus);
635 if (DBType != MDBType.EXPLICIT && DBType != MDBType.UNKNOWN)
640 // Update (or disable) databases defined by "mdb.dir" in
642 private static void update_list (int list_idx)
644 M17n.DebugPrint ("Updating list: {0}\n", list_idx);
645 // At first disable all target databases.
646 foreach (KeyValuePair<Tag, List<MDatabase>> kv in wdict)
647 foreach (MDatabase mdb in kv.Value)
648 if (mdb.ListIndex == list_idx)
650 M17n.DebugPrint ("deleting: {0}\n", mdb);
651 mdb.DBStatus = MDBStatus.INVALID;
654 foreach (KeyValuePair<Tag, List<MDatabase>> kv in ndict)
655 foreach (MDatabase mdb in kv.Value)
656 if (mdb.ListIndex == list_idx)
658 M17n.DebugPrint ("deleting: {0}\n", mdb);
659 mdb.DBStatus = MDBStatus.INVALID;
663 FileInfo dblist = DBDirs[list_idx].ListInfo;
668 using (FileStream stream = File.OpenRead (dblist.FullName))
670 plist = new MPlist (stream);
674 foreach (MPlist pl in plist)
680 Tag tag = new Tag (ref p);
681 MDatabaseInfo info = new MDatabaseInfo (p);
682 int dir_idx = Path.IsPathRooted (info.Filename) ? 0 : -1;
683 register (list_idx, dir_idx, tag, info);
687 Console.WriteLine (e.Message + ": " + pl.Plist);
692 // Update (or disable) databases in DBDirs[dir_idx].
693 private static void update_dir (int dir_idx)
695 M17n.DebugPrint ("Updating dir: {0}\n", dir_idx);
696 // Reset all databases in DBDirs[dir_idx].
697 foreach (KeyValuePair<Tag, List<MDatabase>> kv in ndict)
698 foreach (MDatabase mdb in kv.Value)
699 if (mdb.DirIndex >= dir_idx)
701 M17n.DebugPrint ("disabling: {0}\n", mdb);
702 mdb.DBStatus = MDBStatus.NOT_READY;
705 // Re-expand all WILDCARD databases in DBDirs[dir_idx].
706 if (DBDirs[dir_idx].DirInfo != null)
707 foreach (KeyValuePair<Tag, List<MDatabase>> kv in wdict)
708 foreach (MDatabase mdb in kv.Value)
709 if (mdb.DBStatus == MDBStatus.READY)
711 M17n.DebugPrint ("re-expanding: {0}\n", mdb);
712 register_files (DBDirs[dir_idx].Dirname, mdb.ListIndex,
717 private static void register_files (string dir, int list_idx, int dir_idx,
718 MDatabaseInfo base_info)
720 List<FileInfo> files = new List<FileInfo> ();
721 MGlob.FileList (ref files, dir, base_info.Filename);
722 foreach (FileInfo fileinfo in files)
725 using (FileStream stream = fileinfo.OpenRead ())
727 plist = new MPlist (stream, 1);
729 if (plist != null && plist.IsPlist)
732 Tag tag = new Tag (ref plist);
734 if (! tag.HasWildcard && tag.Match (tag))
736 MDatabaseInfo info = new MDatabaseInfo (plist);
737 info.Merge (base_info);
738 if (Path.IsPathRooted (base_info.Filename))
739 info.Filename = fileinfo.FullName;
741 info.Filename = fileinfo.Name;
742 register (list_idx, dir_idx, tag, info);
748 private void expand_wildcard ()
750 M17n.DebugPrint ("expanding: {0}\n", this);
753 register_files (null, ListIndex, DirIndex, Info);
755 for (int i = 1; i < 4; i++)
756 if (DBDirs[i].DirInfo != null)
757 register_files (DBDirs[i].DirInfo.FullName, ListIndex, i, Info);
758 DBStatus = MDBStatus.READY;
761 private static void maybe_expand_wildcard (Tag tag)
763 foreach (KeyValuePair<Tag, List<MDatabase>> kv in wdict)
765 M17n.DebugPrint ("expand check: {0}\n", kv.Key);
766 if (kv.Key.Match (tag))
768 foreach (MDatabase mdb in kv.Value)
770 if (mdb.DBStatus == MDBStatus.NOT_READY)
771 mdb.expand_wildcard ();
777 private bool update_status ()
779 if (DBType == MDBType.UNKNOWN)
781 if (DBStatus == MDBStatus.INVALID)
783 if (DBStatus == MDBStatus.READY)
786 || File.Exists (FileInfo.FullName))
788 DBStatus = MDBStatus.INVALID;
791 for (int i = 1; i < 4; i++)
792 if (DBDirs[i] != null && DBDirs[i].Dirname != null)
794 string filename = Path.Combine (DBDirs[i].Dirname, Info.Filename);
795 if (File.Exists (filename))
797 FileInfo = new FileInfo (filename);
805 public static MDatabase Find (Tag tag)
807 List<MDatabase> mdbs;
808 MDatabase mdb = null;
811 throw new ArgumentException ("Wildcard not allowed: " + tag);
813 if (ndict.TryGetValue (tag, out mdbs))
816 for (int i = 0; i < mdbs.Count; i++)
819 if (mdb.ListIndex == 0)
821 if (mdb.DBStatus == MDBStatus.READY)
825 if (! update_all () && mdb != null)
827 maybe_expand_wildcard (tag);
828 if (! ndict.TryGetValue (tag, out mdbs))
830 for (int i = 0; i < mdbs.Count; i++)
831 if ((mdb = mdbs[i]).update_status ())
836 public static List<MDatabase> List (Tag tag)
838 List<MDatabase> list = new List<MDatabase> ();
841 maybe_expand_wildcard (tag);
845 foreach (KeyValuePair<Tag, List<MDatabase>> kv in ndict)
846 if (kv.Key.Match (tag))
847 foreach (MDatabase mdb in kv.Value)
848 if (mdb.update_status ())
856 List<MDatabase> mdbs;
857 if (ndict.TryGetValue (tag, out mdbs))
858 foreach (MDatabase mdb in mdbs)
859 if (mdb.update_status ())
868 public object Load ()
871 return loader (tag, ExtraInfo);
872 if (Info.Format == Mchar_table)
873 throw new Exception ("Use Load (MCharTable) to load this database");
874 if (Info.Format == Mcharset)
875 throw new Exception ("Use Load (MCharset) to load this database");
876 if (! update_status ())
877 throw new Exception ("Database invalid");
880 using (FileStream stream = File.OpenRead (FileInfo.FullName))
881 plist = new MPlist (stream);
885 public object Load (MSymbol key, MSymbol stop)
887 if (loader != null || Info.Format != MSymbol.plist)
888 throw new ArgumentException
889 ("Key can't be specified for loading this database");
890 if (! update_status ())
891 throw new Exception ("Database invalid");
893 using (FileStream stream = File.OpenRead (FileInfo.FullName))
894 plist = new MPlist (stream, key, stop);
898 /// <summary>Return a list of currently available database
899 /// directory names</summary>.
900 public static string[] DirectoryList ()
902 List<string> dirs = new List<string> ();
904 for (int i = 1; i < 4; i++)
905 if (DBDirs[i].Dirname != null)
906 dirs.Add (DBDirs[i].Dirname);
907 return dirs.ToArray ();
910 // For IComparable<MDatabase>
911 public int CompareTo (MDatabase other)
913 return (ListIndex == other.ListIndex
914 ? DirIndex - other.DirIndex
915 : ListIndex - other.ListIndex);