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 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 ("[NDITCT]");
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)
518 mdbs[i].DBStatus = MDBStatus.INVALID;
526 mdbs = new List<MDatabase> (1);
528 dict.Add (mdb.tag, mdbs);
532 private static void register (int list_idx, int dir_idx,
533 Tag tag, MDatabaseInfo info)
535 List<MDatabase> mdbs;
538 if (ndict.TryGetValue (tag, out mdbs))
539 for (int i = 0; i < mdbs.Count; i++)
542 if (mdb.ListIndex == list_idx)
544 if (mdb.DBStatus == MDBStatus.INVALID)
545 M17n.DebugPrint ("registering: {0}\n", mdb);
547 M17n.DebugPrint ("updating: {0}\n", mdb);
548 mdb.DBType = dir_idx == -1 ? MDBType.AUTO : MDBType.MULTIPLE;
549 mdb.DBStatus = MDBStatus.NOT_READY;
550 mdb.DirIndex = dir_idx;
557 mdbs = new List<MDatabase> (1);
558 ndict.Add (tag, mdbs);
560 mdb = new MDatabase (list_idx, dir_idx, tag, info);
561 M17n.DebugPrint ("registering: {0}\n", mdb);
565 public MDatabase (Tag tag, Loader loader, object extra_info)
568 this.loader = loader;
569 DBType = MDBType.UNKNOWN;
570 DBStatus = MDBStatus.READY;
573 ExtraInfo = extra_info;
577 public MDatabase (Tag tag, string filename)
580 DBType = MDBType.EXPLICIT;
581 if (Path.IsPathRooted (filename))
584 DBStatus = MDBStatus.READY;
589 DBStatus = MDBStatus.NOT_READY;
592 Info = new MDatabaseInfo ();
593 Info.Filename = filename;
597 private MDatabase (int list_idx, int dir_idx, Tag tag, MDatabaseInfo info)
601 DBType = this.tag.HasWildcard ? MDBType.WILDCARD : MDBType.AUTO;
602 if (this.tag[0] == Mchar_table || this.tag[0] == Mcharset)
603 Info.Format = this.tag[0];
604 ListIndex = list_idx;
606 if (Path.IsPathRooted (Info.Filename))
607 DBStatus = MDBStatus.READY;
609 DBStatus = MDBStatus.NOT_READY;
612 public override String ToString () {
613 string str = ("#<MDataBase (" + ListIndex + "," + DirIndex + ") "
614 + tag + " " + DBType + " " + DBStatus);
616 if (DBType != MDBType.EXPLICIT && DBType != MDBType.UNKNOWN)
621 // Update (or disable) databases defined by "mdb.dir" in
623 private static void update_list (int list_idx)
625 // At first disable all target databases.
626 foreach (KeyValuePair<Tag, List<MDatabase>> kv in wdict)
627 foreach (MDatabase mdb in kv.Value)
628 if (mdb.ListIndex == list_idx)
630 M17n.DebugPrint ("deleting: {0}\n", mdb);
631 mdb.DBStatus = MDBStatus.INVALID;
634 foreach (KeyValuePair<Tag, List<MDatabase>> kv in ndict)
635 foreach (MDatabase mdb in kv.Value)
636 if (mdb.ListIndex == list_idx)
638 M17n.DebugPrint ("deleting: {0}\n", mdb);
639 mdb.DBStatus = MDBStatus.INVALID;
643 FileInfo dblist = DBDirs[list_idx].ListInfo;
648 using (FileStream stream = File.OpenRead (dblist.FullName))
650 MStreamReader reader = new MStreamReader (stream);
651 plist = new MPlist (reader);
655 foreach (MPlist pl in plist)
661 Tag tag = new Tag (ref p);
662 MDatabaseInfo info = new MDatabaseInfo (p);
663 int dir_idx = Path.IsPathRooted (info.Filename) ? 0 : -1;
664 register (list_idx, dir_idx, tag, info);
668 Console.WriteLine (e.Message + ": " + pl.Plist);
673 // Update (or disable) databases in DBDirs[dir_idx].
674 private static void update_dir (int dir_idx)
676 // Reset all databases in DBDirs[dir_idx].
677 foreach (KeyValuePair<Tag, List<MDatabase>> kv in ndict)
678 foreach (MDatabase mdb in kv.Value)
679 if (mdb.DirIndex >= dir_idx)
681 M17n.DebugPrint ("disabling: {0}\n", mdb);
682 mdb.DBStatus = MDBStatus.NOT_READY;
685 // Re-expand all WILDCARD databases in DBDirs[dir_idx].
686 if (DBDirs[dir_idx].DirInfo != null)
687 foreach (KeyValuePair<Tag, List<MDatabase>> kv in wdict)
688 foreach (MDatabase mdb in kv.Value)
689 if (mdb.DBStatus == MDBStatus.READY)
691 M17n.DebugPrint ("re-expanding: {0}\n", mdb);
692 register_files (DBDirs[dir_idx].Dirname, mdb.ListIndex,
697 private static void register_files (string dir, int list_idx, int dir_idx,
698 MDatabaseInfo base_info)
700 List<FileInfo> files = new List<FileInfo> ();
701 MGlob.FileList (ref files, dir, base_info.Filename);
702 foreach (FileInfo fileinfo in files)
705 using (FileStream stream = fileinfo.OpenRead ())
707 MStreamReader reader = new MStreamReader (stream);
708 plist = new MPlist (reader, 1);
710 if (plist != null && plist.IsPlist)
713 Tag tag = new Tag (ref plist);
715 if (! tag.HasWildcard && tag.Match (tag))
717 MDatabaseInfo info = new MDatabaseInfo (plist);
718 info.Merge (base_info);
719 if (Path.IsPathRooted (base_info.Filename))
720 info.Filename = fileinfo.FullName;
722 info.Filename = fileinfo.Name;
723 register (list_idx, dir_idx, tag, info);
729 private void expand_wildcard ()
731 M17n.DebugPrint ("expanding: {0}\n", this);
734 register_files (null, ListIndex, DirIndex, Info);
736 for (int i = 1; i < 4; i++)
737 if (DBDirs[i].DirInfo != null)
738 register_files (DBDirs[i].DirInfo.FullName, ListIndex, i, Info);
739 DBStatus = MDBStatus.READY;
742 private static void maybe_expand_wildcard (Tag tag)
744 foreach (KeyValuePair<Tag, List<MDatabase>> kv in wdict)
745 if (kv.Key.Match (tag))
746 foreach (MDatabase mdb in kv.Value)
747 if (mdb.DBStatus == MDBStatus.NOT_READY)
748 mdb.expand_wildcard ();
751 private bool update_status ()
753 if (DBType == MDBType.UNKNOWN)
755 if (DBStatus == MDBStatus.READY
759 if (CheckedTime >= FileInfo.LastWriteTime)
765 = DBDirs[DirIndex].DirInfo.GetFiles (Info.Filename);
772 if (DBDirs[DirIndex].DirInfo == null)
774 FileInfo[] files = DBDirs[DirIndex].DirInfo.GetFiles (Info.Filename);
775 if (files.Length == 0)
781 private bool find_file ()
790 public static MDatabase Find (Tag tag)
792 List<MDatabase> mdbs;
793 MDatabase mdb = null;
796 throw new ArgumentException ("Wildcard not allowed: " + tag);
798 if (ndict.TryGetValue (tag, out mdbs))
801 for (int i = 0; i < mdbs.Count; i++)
804 if (mdb.ListIndex == 0)
806 if (mdb.DBStatus == MDBStatus.READY)
810 if (! update_all () && mdb != null)
812 maybe_expand_wildcard (tag);
813 if (! ndict.TryGetValue (tag, out mdbs))
815 for (int i = 0; i < mdbs.Count; i++)
816 if ((mdb = mdbs[i]).check_file ())
821 public static List<MDatabase> List (Tag tag)
823 List<MDatabase> list = new List<MDatabase> ();
826 maybe_expand_wildcard (tag);
830 foreach (KeyValuePair<Tag, List<MDatabase>> kv in ndict)
831 if (kv.Key.Match (tag))
832 foreach (MDatabase mdb in kv.Value)
833 if (mdb.check_file ())
841 List<MDatabase> mdbs;
842 if (ndict.TryGetValue (tag, out mdbs))
843 foreach (MDatabase mdb in mdbs)
844 if (mdb.check_file ())
853 public object Load ()
855 return (loader != null ? loader (tag, ExtraInfo)
856 : load (MSymbol.nil, MSymbol.nil));
859 public object Load (MSymbol key, MSymbol stop)
863 return load (key, stop);
866 private object load (MSymbol key, MSymbol stop)
871 // For IComparable<MDatabase>
872 public int CompareTo (MDatabase other)
874 return (ListIndex == other.ListIndex
875 ? DirIndex - other.DirIndex
876 : ListIndex - other.ListIndex);