2 using System.Collections.Generic;
9 internal class MDatabaseDir
11 private const string ListFileName = "mdb.dir";
13 public string Dirname;
14 public DirectoryInfo DirInfo;
15 public FileInfo ListInfo;
16 public DateTime LastScanned;
18 public MDatabaseDir (string dirname)
24 DirInfo = new DirectoryInfo (dirname);
26 ListInfo = DirInfo.GetFiles (ListFileName)[0];
37 public bool CheckStatus ()
46 LastScanned = new DateTime (0);
59 ListInfo = DirInfo.GetFiles (ListFileName)[0];
65 return (LastScanned < DirInfo.LastWriteTime
67 && LastScanned < ListInfo.LastWriteTime));
71 if (Dirname != null && Directory.Exists (Dirname))
73 DirInfo = new DirectoryInfo (Dirname);
75 ListInfo = DirInfo.GetFiles (ListFileName)[0];
85 public void UpdateStatus ()
88 LastScanned = DateTime.UtcNow;
91 public FileInfo[] Scan (string filename)
96 return DirInfo.GetFiles (filename);
100 public class MDatabase
102 /// Tags to identify a MDatabase.
105 public MSymbol[] Tags;
107 public Tag (MSymbol tag0)
109 Tags = new MSymbol[4];
110 Tags[0] = tag0; Tags[1] = Tags[2] = Tags[3] = MSymbol.nil;
113 public Tag (MSymbol tag0, MSymbol tag1)
115 Tags = new MSymbol[4];
116 Tags[0] = tag0; Tags[1] = tag1; Tags[2] = Tags[3] = MSymbol.nil;
119 public Tag (MSymbol tag0, MSymbol tag1, MSymbol tag2)
121 Tags = new MSymbol[4];
122 Tags[0] = tag0; Tags[1] = tag1; Tags[2] = tag2; Tags[3] = MSymbol.nil;
125 public Tag (MSymbol tag0, MSymbol tag1, MSymbol tag2, MSymbol tag3)
127 Tags = new MSymbol[4];
128 Tags[0] = tag0; Tags[1] = tag1; Tags[2] = tag2; Tags[3] = tag3;
131 public bool Match (Tag tag)
133 for (int i = 0; i < 4; i++)
135 if (tag.Tags[i] == Mwildcard || Tags[i] == Mwildcard)
137 if (tag.Tags[i] != Tags[i])
143 public override string ToString ()
146 + Tags[0] + "," + Tags[1] + "," + Tags[2] + "," + Tags[3]
151 public delegate object Loader (Tag tag, object extra_info);
153 internal class MDatabaseInfo {
154 // -2: absolute, -1: unknown 0: DBDirs[0], 1: DBDirs[1], 2: DBDirs[2]
155 internal int DirIndex;
156 internal string Description;
157 internal string Filename;
158 internal FileInfo FileInfo;
159 internal FileInfo Validater;
160 internal int Version;
161 internal MSymbol Format;
162 internal MSymbol Schema;
163 internal string SchemaFile;
164 internal DateTime ModifiedTime;
165 internal MPlist Props;
167 public MDatabaseInfo ()
169 Format = Schema = MSymbol.nil;
173 public override string ToString ()
175 string str = ("#<Info " + Format + " \"" + Filename
176 + "\" (" + DirIndex + ")");
177 if (Schema != MSymbol.nil)
183 private static Dictionary<MDatabase.Tag, MDatabase[]> DBDict
184 = new Dictionary<MDatabase.Tag, MDatabase[]> ();
186 private static Dictionary<MDatabase.Tag, MDatabase[]> DBDictMulti
187 = new Dictionary<MDatabase.Tag, MDatabase[]> ();
189 private static MDatabaseDir[] DBDirs = new MDatabaseDir[3];
191 private static readonly MSymbol Mversion = MSymbol.Of ("version");
193 private static readonly MSymbol Mwildcard = MSymbol.Of ("*");
194 private static readonly MSymbol Mchar_table = MSymbol.Of ("char-table");
195 private static readonly MSymbol Mcharset = MSymbol.Of ("charset");
200 /// The database was defined automatically from one of mdb.dir
201 /// files with no wildcard tag.
203 /// The database was defined automatically from one of mdb.dir
204 /// files with a wildcard tag to define multiple databases
205 /// of the same kind.
207 /// The database was defined explicitely by MDatabase.Define
208 /// to use the normal loader.
210 /// The database was defined explicitely by MDatabase.Define
211 /// to use a special loader.
213 /// The database is for defining multiple databases of the
214 /// same kind with a wildcard tag.
218 /// Status of database
219 private enum MDBStatus
221 // The database file is currently disabled. It means that the
222 // database file is not readable or the database is deleted by
223 // the modification of "mdb.dir".
225 // The database file has not yet been loaded, or was modified
226 // after the previous loading.
228 // The database file has not been modified after the previous
231 // The database file is updated but the validation was failed
232 // or the version is not supported by the current system.
237 private Loader loader;
238 private object ExtraInfo;
239 private MDBType DBType;
240 private MDBStatus DBStatus;
241 internal DateTime LoadedTime;
242 internal MDatabaseInfo Info;
246 string share_dir = (Environment.GetFolderPath
247 (Environment.SpecialFolder.CommonApplicationData));
248 string usr_dir = (Environment.GetFolderPath
249 (Environment.SpecialFolder.ApplicationData));
252 DBDirs[0] = new MDatabaseDir (Path.Combine (usr_dir, ".m17n.d"));
253 } catch (ArgumentException) {
254 DBDirs[0] = new MDatabaseDir (Path.Combine (usr_dir, "_m17n_d"));
256 DBDirs[1] = new MDatabaseDir (null);
257 DBDirs[2] = new MDatabaseDir (Path.Combine (share_dir, "m17n"));
260 public static string ApplicationDir
261 { get { return (DBDirs[1].Dirname); }
262 set { DBDirs[1].Dirname = value; DBDirs[1].CheckStatus (); } }
264 private static bool update_database_directories ()
266 bool updated = false;
267 for (int i = 0; i < 3; i++)
268 if (DBDirs[i].CheckStatus ())
270 delete_databases (i);
271 if (DBDirs[i].ListInfo != null)
272 update_databases (i);
278 public static void ListDirs ()
280 update_database_directories ();
281 for (int i = 0; i < 3; i++)
282 if (DBDirs[i].Dirname != null)
284 Console.Write ("{0}:{1}", i, DBDirs[i].Dirname);
285 if (DBDirs[i].DirInfo != null)
287 if (DBDirs[i].ListInfo != null)
288 Console.WriteLine (" {0}", DBDirs[i].ListInfo);
290 Console.WriteLine (".. no mdb.dir");
293 Console.WriteLine (".. not exist");
297 private void register (int priority)
299 Dictionary<MDatabase.Tag, MDatabase[]> dict
300 = DBType == MDBType.WILDCARD ? DBDictMulti : DBDict;
303 if (! dict.TryGetValue (tag, out mdbs))
305 mdbs = new MDatabase[4];
306 dict.Add (tag, mdbs);
308 mdbs[priority] = this;
311 public MDatabase (Tag tag, Loader loader, object extra_info)
314 this.loader = loader;
315 DBType = MDBType.UNKNOWN;
316 DBStatus = MDBStatus.UPDATED;
317 ExtraInfo = extra_info;
321 public MDatabase (Tag tag, string filename)
324 DBType = MDBType.EXPLICIT;
325 DBStatus = MDBStatus.OUTDATED;
326 Info = new MDatabaseInfo ();
327 Info.Filename = filename;
328 Info.DirIndex = Path.IsPathRooted (filename) ? -2 : -1;
332 private MDatabase (MPlist plist, int priority)
334 tag = new Tag (MSymbol.nil);
335 DBType = MDBType.AUTO;
336 DBStatus = MDBStatus.OUTDATED;
337 for (int i = 0; plist.IsSymbol; i++, plist = plist.Next)
339 if (DBType == MDBType.WILDCARD)
340 tag.Tags[i] = MSymbol.nil;
343 tag.Tags[i] = plist.Symbol;
344 if (tag.Tags[i] == Mwildcard)
345 DBType = MDBType.WILDCARD;
349 Info = new MDatabaseInfo ();
350 if (tag.Tags[0] == Mchar_table || tag.Tags[0] == Mcharset)
351 Info.Format = tag.Tags[0];
353 Info.Format = MSymbol.plist;
356 Info.Filename = plist.Text.ToString ();
359 else if (plist.IsPlist)
361 MPlist p = plist.Plist;
364 Info.Filename = plist.Text.ToString ();
369 Info.Format = p.Symbol;
374 Info.Schema = p.Symbol;
377 Info.SchemaFile = p.Text.ToString ();
383 throw new Exception ("Invalid source definition:" + plist);
385 Info.DirIndex = Path.IsPathRooted (Info.Filename) ? -2 : -1;
387 Info.Props = new MPlist ();
388 foreach (MPlist pl in plist)
394 if (p.IsSymbol && p.Symbol == Mversion)
396 Info.Version = parse_version (p.Next);
397 if (M17n.Version < Info.Version)
398 DBStatus = MDBStatus.DISABLED;
401 Info.Props.Put (pl.Key, pl.Val);
403 this.register (priority);
406 public override String ToString () {
407 string str = "#<MDataBase " + tag + " " + DBType + " " + DBStatus;
409 if (DBType != MDBType.EXPLICIT && DBType != MDBType.UNKNOWN)
414 private static int parse_version (MPlist plist)
417 int major, minor, release;
421 str = plist.Text.ToString ().Split ('.');
424 try { major = int.Parse (str[0]); } catch { return 0xFFFFFF; }
425 try { minor = int.Parse (str[1]); } catch { return 0xFFFFFF; }
426 try { release = int.Parse (str[2]); } catch { return 0xFFFFFF; }
427 return ((major << 16) | (minor << 8) | release);
430 private static void delete_databases (int list_idx)
432 foreach (KeyValuePair<Tag, MDatabase[]> item in DBDict)
433 item.Value[list_idx + 1] = null;
434 foreach (KeyValuePair<Tag, MDatabase[]> item in DBDictMulti)
435 item.Value[list_idx + 1] = null;
438 private static void update_databases (int list_idx)
440 MDatabaseDir dbdir = DBDirs[list_idx];
441 FileInfo dblist = dbdir.ListInfo;
444 using (FileStream stream = File.OpenRead (dblist.FullName))
446 MStreamReader reader = new MStreamReader (stream);
447 plist = new MPlist (reader);
451 foreach (MPlist pl in plist)
457 new MDatabase (pl.Plist, list_idx + 1);
461 Console.WriteLine (e.Message);
466 private void expand_wildcard (int priority)
468 if (Info.DirIndex == -2)
475 private void update_status ()
477 if (Info.DirIndex == -2)
482 public static MDatabase Find (Tag tag)
485 MDatabase mdb = null;
487 if (DBDict.TryGetValue (tag, out mdbs))
491 for (int i = 1; i < 4; i++)
492 if ((mdb = mdbs[i]) != null)
495 if (! update_database_directories ())
497 if (! DBDict.TryGetValue (tag, out mdbs))
499 for (int i = 1; i < 4; i++)
505 public static List<MDatabase> List (Tag tag)
507 List<MDatabase> list = new List<MDatabase> ();
509 update_database_directories ();
510 foreach (KeyValuePair<Tag, MDatabase[]> item in DBDictMulti)
511 if (item.Key.Match (tag))
512 for (j = 0; j < 4; j++)
513 if (item.Value[j] != null)
514 if (item.Value[j].DBStatus == MDBStatus.OUTDATED)
515 item.Value[j].expand_wildcard (j);
517 for (int i = 0; i < 4 && tag.Tags[i] == Mwildcard; i++);
521 if (DBDict.TryGetValue (tag, out mdbs))
522 for (int j = 0; j < 4; j++)
525 mdbs[j].update_status ();
526 if (mdbs[j].DBStatus != MDBStatus.DISABLED)
535 // With wildcard. We must scan all databases.
536 foreach (KeyValuePair<Tag, MDatabase[]> item in DBDict)
537 if (item.Key.Match (tag))
538 for (int j = 0; j < 4; j++)
539 if (item.Value[j] != null)
541 MDatabase mdb = item.Value[j];
542 mdb.update_status ();
543 if (mdb.DBStatus != MDBStatus.DISABLED)
553 public object Load ()
555 return (loader != null ? loader (tag, ExtraInfo)
556 : load (MSymbol.nil, MSymbol.nil));
559 public object Load (MSymbol key, MSymbol stop)
563 return load (key, stop);
566 private object load (MSymbol key, MSymbol stop)
568 LoadedTime = DateTime.UtcNow;