74c3ff389627ab3d80005353ea697bf0fd44d572
[m17n/m17n-lib-cs.git] / MDatabase.cs
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using M17N;
5 using M17N.Core;
6
7 namespace M17N.Core
8 {
9   public delegate object MDatabaseLoader (MDatabaseTag tag,
10                                           object extra_info);
11
12   public struct MDatabaseTag
13   {
14     public MSymbol Tag0, Tag1, Tag2, Tag3;
15
16     public MDatabaseTag (MSymbol tag0)
17       {
18         Tag0 = tag0; Tag1 = Tag2 = Tag3 = MSymbol.nil;
19       }
20
21     public MDatabaseTag (MSymbol tag0, MSymbol tag1)
22       {
23         Tag0 = tag0; Tag1 = tag1; Tag2 = Tag3 = MSymbol.nil;
24       }
25
26     public MDatabaseTag (MSymbol tag0, MSymbol tag1, MSymbol tag2)
27       {
28         Tag0 = tag0; Tag1 = tag1; Tag2 = tag2; Tag3 = MSymbol.nil;
29       }
30
31     public MDatabaseTag (MSymbol tag0, MSymbol tag1,
32                          MSymbol tag2, MSymbol tag3)
33       {
34         Tag0 = tag0; Tag1 = tag1; Tag2 = tag2; Tag3 = tag3;
35       }
36   }
37
38   public class MDatabase
39   {
40     private class MDatabaseDir
41     {
42       private const string ListFileName = "mdb.dir";
43
44       public string Dirname;
45       public DirectoryInfo DirInfo;
46       public DateTime LastScanned;
47       public FileInfo ListInfo;
48
49       private static void GetInfo (string dirname, out DirectoryInfo dirinfo,
50                                    out FileInfo listinfo)
51       {
52         if (Directory.Exists (dirname))
53           try { dirinfo = new DirectoryInfo (dirname);
54             try { listinfo = dirinfo.GetFiles (ListFileName)[0];
55             } catch { listinfo = null; }
56           } catch { dirinfo = null; listinfo = null; }
57         else
58           {
59             dirinfo = null;
60             listinfo = null;
61           }
62       }
63
64       public MDatabaseDir (string dirname)
65       {
66         Dirname = dirname;
67         GetInfo (dirname, out DirInfo, out ListInfo);
68       }
69
70       public bool StatusChanged {
71         get {
72           bool exists = Directory.Exists (Dirname);
73
74           if (DirInfo != null)
75             {
76               if (! exists)
77                 {
78                   DirInfo = null;
79                   ListInfo = null;
80                   LastScanned = new DateTime (0);
81                 }
82               if (LastScanned.Year == 0)
83                 return true;
84               DirInfo.Refresh ();
85               if (ListInfo != null)
86                 ListInfo.Refresh ();
87               return (LastScanned < DirInfo.LastWriteTime
88                       || LastScanned < ListInfo.LastWriteTime);
89             }
90           else
91             {
92               if (exists)
93                 {
94                   DirInfo = new DirectoryInfo (Dirname);
95                   try {
96                     ListInfo = DirInfo.GetFiles (ListFileName)[0];
97                   } catch {
98                     ListInfo = null;
99                   }
100                   return true;
101                 }
102               return false;
103             }
104         }
105       }
106
107       public void UpdateStatus ()
108       {
109         if (DirInfo != null)
110           LastScanned = DateTime.UtcNow;
111       }
112
113       public FileInfo[] Scan (string filename)
114       {
115         if (DirInfo == null)
116           return null;
117         DirInfo.Refresh ();
118         return DirInfo.GetFiles (filename);
119       }
120     }
121
122     internal class MDatabaseInfo {
123       internal DirectoryInfo Dir;
124       internal string Description;
125       internal MText Filename;
126       internal FileInfo FileInfo;
127       internal FileInfo Validater;
128       internal int Version;
129       internal MSymbol Format;
130       internal MSymbol Schema;
131       internal MText SchemaFile;
132       internal DateTime ModifiedTime;
133       internal MPlist Props;
134     }
135
136     private static Dictionary<MDatabaseTag, MDatabase> DBDict
137       = new Dictionary<MDatabaseTag, MDatabase> ();
138
139     private static MDatabaseDir[] DBDirs = new MDatabaseDir[3];
140
141     private const string SystemDirectory = "/usr/share/m17n";
142     private readonly MSymbol Mversion = new MSymbol ("version");
143
144     /// Type of database
145     private enum MDBType
146       {
147         /// The database was defined automatically from mdb.dir
148         /// file(s) with no wildcard tag.
149         AUTO,
150         /// The database was defined automatically from mdb.dir
151         /// file(s) with a wildcard tag to define multiple databases
152         /// of the same kind.
153         MULTIPLE,
154         /// The database was defined explicitely by MDatabase.Define
155         /// without a special loader.
156         EXPLICIT,
157         /// The database was defined explicitely by MDatabase.Define
158         /// with a special loader.
159         UNKNOWN,
160       };
161
162     /// Status of database
163     private enum MDBStatus
164       {
165         // The database file is currently disabled.  It means that the
166         // database file is not readable or the database is deleted by
167         // the modification of "mdb.dir".
168         DISABLED,
169         // The database file has not yet been loaded, or was modified
170         // after the previous loading.
171         OUTDATED,
172         // The database file has not been modified after the previous
173         // loading.
174         UPDATED,
175         // The database file is updated but the validation was failed
176         // or the version is not supported by the current system.
177         INVALID,
178       };
179
180     public readonly MDatabaseTag Tag;
181     private MDatabaseLoader Loader;
182     private object ExtraInfo;
183     private MDBType DBType;
184     private MDBStatus DBStatus;
185     internal DateTime LoadedTime;
186     internal MDatabaseInfo Info;
187
188     public static string ApplicationDirectory;
189
190     private MDatabase (MDatabaseTag tag, MDatabaseLoader loader,
191                       object extra_info)
192     {
193       Tag = tag;
194       Loader = loader;
195       ExtraInfo = extra_info;
196     }
197
198     private MDatabase (MDatabaseTag tag, string filename)
199     {
200       Tag = tag;
201       Info = new MDatabaseInfo ();
202       Info.Filename = new MText (filename);
203     }
204
205     private MDatabase (MPlist plist)
206     {
207       MSymbol[] tags = new MSymbol[4];
208       int i;
209
210       for (i = 0; plist.IsSymbol; i++, plist = plist.Next)
211         tags[i] = plist.Symbol;
212       while (i < 4)
213         tags[i++] = MSymbol.nil;
214       Tag = new MDatabaseTag (tags[0], tags[1], tags[2], tags[3]);
215       if (plist.IsMText)
216         {
217           Info.Filename = plist.Text;
218           plist = plist.Next;
219         }
220       else if (plist.IsPlist)
221         {
222           MPlist p = plist.Plist;
223
224           if (p.IsMText)
225             Info.Filename = plist.Text;
226           p = p.Next;
227           if (! p.IsEmpty)
228             {
229               if (p.IsSymbol)
230                 Info.Format = p.Symbol;
231               p = p.Next;
232               if (! p.IsEmpty)
233                 {
234                   if (p.IsSymbol)
235                     Info.Schema = p.Symbol;
236                   p = p.Next;
237                   if (p.IsMText)
238                     Info.SchemaFile = p.Text;
239                 }                   
240             }
241           plist = plist.Next;
242         }
243       DBStatus = MDBStatus.OUTDATED;;
244       Info.Version = 0;
245       Info.Props = new MPlist ();
246       foreach (MPlist pl in plist)
247         {
248           if (pl.IsPlist)
249             {
250               MPlist p = pl.Plist;
251               
252               if (p.IsSymbol && p.Symbol == Mversion)
253                 {
254                   Info.Version = parse_version (p.Next);
255                   if (M17n.Version < Info.Version)
256                     DBStatus = MDBStatus.DISABLED;
257                 }
258             }
259           Info.Props.Put (pl.Key, pl.Val);
260         }
261     }
262
263     private static int parse_version (MPlist plist)
264     {
265       string[] str;
266       int major, minor, release;
267
268       if (! plist.IsMText)
269         return 0xFFFFFF;
270       str = plist.Text.ToString ().Split ('.');
271       if (str.Length != 3)
272         return 0xFFFFFF;
273       try { major = int.Parse (str[0]); } catch { return 0xFFFFFF; }
274       try { minor = int.Parse (str[1]); } catch { return 0xFFFFFF; }
275       try { release = int.Parse (str[2]); } catch { return 0xFFFFFF; }
276       return ((major << 16) | (minor << 8) | release);
277     }
278
279     public static MDatabase Define (MDatabaseTag tag, MDatabaseLoader loader,
280                                     object extra_info)
281     {
282       MDatabase db = MDatabase.Find (tag);
283
284       if (db != null)
285         {
286           db.Loader = loader;
287           db.ExtraInfo = extra_info;
288           db.DBType = MDBType.EXPLICIT;
289           db.DBStatus = MDBStatus.OUTDATED;
290           db.Info = null;
291           return db;
292         }
293       return new MDatabase (tag, loader, extra_info);
294     }
295
296     public static  MDatabase Define (MDatabaseTag tag, string filename)
297     {
298       MDatabase db = MDatabase.Find (tag);
299
300       if (db != null)
301         {
302           db.Loader = null;
303           db.DBType = MDBType.EXPLICIT;
304           db.DBStatus = MDBStatus.OUTDATED;
305           db.Info = new MDatabaseInfo ();
306           db.Info.Filename = new MText (filename);
307
308           return db;
309         }
310       return new MDatabase (tag, filename);
311     }
312
313     static MDatabase ()
314     {
315       string share_dir = (Environment.GetFolderPath
316                           (Environment.SpecialFolder.CommonApplicationData));
317       string usr_dir = (Environment.GetFolderPath
318                         (Environment.SpecialFolder.ApplicationData));
319
320       try {
321         DBDirs[0] = new MDatabaseDir (Path.Combine (usr_dir, ".m17n.d"));
322       } catch (ArgumentException) {
323         DBDirs[0] = new MDatabaseDir (Path.Combine (usr_dir, "_m17n_d"));
324       }
325       DBDirs[1] = null;
326       DBDirs[2] = new MDatabaseDir (Path.Combine (share_dir, "m17n"));
327     }
328
329     public static MDatabase Find (MDatabaseTag tag)
330     {
331       MDatabase db;
332
333       return (DBDict.TryGetValue (tag, out db) ? db : null);
334     }
335
336     public object Load ()
337     {
338       return (Loader != null ? Loader (Tag, ExtraInfo)
339               : load (MSymbol.nil, MSymbol.nil));
340     }
341
342     public object Load (MSymbol key, MSymbol stop)
343     {
344       if (Loader != null)
345         return null;
346       return load (key, stop);
347     }
348
349     private object load (MSymbol key, MSymbol stop)
350     {
351       LoadedTime = DateTime.UtcNow;
352
353       
354
355       return null;
356     }
357
358   }
359
360 }