Tizen 2.1 base
[external/enchant.git] / src / bindings / Enchant.Net / Broker.cs
1 /* Copyright (c) 2007 Eric Scott Albright\r
2  * \r
3  * Permission is hereby granted, free of charge, to any person obtaining a copy\r
4  * of this software and associated documentation files (the "Software"), to deal\r
5  * in the Software without restriction, including without limitation the rights\r
6  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
7  * copies of the Software, and to permit persons to whom the Software is\r
8  * furnished to do so, subject to the following conditions:\r
9  * \r
10  * The above copyright notice and this permission notice shall be included in\r
11  * all copies or substantial portions of the Software.\r
12  * \r
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
18  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
19  * THE SOFTWARE.\r
20  */\r
21 \r
22 using System;\r
23 using System.Collections.Generic;\r
24 \r
25 namespace Enchant\r
26 {\r
27         public sealed class Broker : IDisposable\r
28         {\r
29                 private readonly SafeBrokerHandle _handle;\r
30                 private IList<DictionaryInfo> _dictionaries;\r
31                 private bool _disposed = false;\r
32                 private IList<ProviderInfo> _providers;\r
33             private readonly Dictionary<string, WeakReference> _dictionaryCache;\r
34             private readonly Dictionary<string, WeakReference> _pwlDictionaryCache;\r
35             private bool _cacheDictionaries = true;\r
36             private static Nullable<bool> _isLibEnchantAvailable;\r
37 \r
38             private static Broker _defaultBroker;\r
39         /// <summary>\r
40         ///  Singleton for apps that want to use that pattern\r
41         /// </summary>\r
42         public static Broker Default\r
43             {\r
44                 get\r
45                 {\r
46                     if(_defaultBroker == null || _defaultBroker._disposed)\r
47                     {\r
48                         _defaultBroker = new Broker();\r
49                     }\r
50                     return _defaultBroker;\r
51                 }\r
52             }\r
53 \r
54         public static bool IsLibEnchantAvailable\r
55         {\r
56             get\r
57             {\r
58                 if (_isLibEnchantAvailable == null)\r
59                 {\r
60                     // force a broker to be created and thus trying to\r
61                     // us libenchant and setting our status\r
62                     Broker broker = Default;\r
63                 }\r
64                 return _isLibEnchantAvailable??false;\r
65             }\r
66         }\r
67 \r
68 \r
69                 public Broker()\r
70                 {\r
71             try\r
72             {\r
73                 _handle = Bindings.enchant_broker_init();\r
74                 _isLibEnchantAvailable = true;\r
75             }\r
76             catch (DllNotFoundException)\r
77             {\r
78                 _isLibEnchantAvailable = false;\r
79             }\r
80             if (_isLibEnchantAvailable == true)\r
81             {\r
82                 VerifyNoErrors();\r
83                 if (_handle.IsInvalid)\r
84                 {\r
85                     throw new ApplicationException("Unable to initialize broker");\r
86                 }\r
87             }\r
88                     _dictionaryCache = new Dictionary<string, WeakReference>();\r
89             _pwlDictionaryCache = new Dictionary<string, WeakReference>();\r
90                 }\r
91 \r
92 \r
93             public IEnumerable<ProviderInfo> Providers\r
94                 {\r
95                         get\r
96                         {\r
97                                 VerifyNotDisposed();\r
98                                 if (_providers == null)\r
99                                 {\r
100                                         InitializeProviderList();\r
101                                 }\r
102                                 return _providers;\r
103                         }\r
104                 }\r
105 \r
106         private void InitializeProviderList()\r
107         {\r
108           _providers = new List<ProviderInfo>();\r
109           if (_isLibEnchantAvailable == true)\r
110           {\r
111               Bindings.enchant_broker_describe(_handle,\r
112                                                delegate(ProviderInfo provider)\r
113                                                { _providers.Add(provider); });\r
114               VerifyNoErrors();\r
115           }\r
116         }\r
117 \r
118         public IEnumerable<DictionaryInfo> Dictionaries\r
119                     {\r
120                             get\r
121                             {\r
122                                     VerifyNotDisposed();\r
123                                     if (_dictionaries == null)\r
124                                     {\r
125                                             InitializeDictionaryList();\r
126                                     }\r
127                                     return _dictionaries;\r
128                             }\r
129                     }\r
130 \r
131         public bool CacheDictionaries\r
132         {\r
133             get { return this._cacheDictionaries; }\r
134             set { this._cacheDictionaries = value; }\r
135         }\r
136 \r
137             private void InitializeDictionaryList()\r
138         {\r
139           _dictionaries = new List<DictionaryInfo>();\r
140           if (_isLibEnchantAvailable == true)\r
141           {\r
142               Bindings.enchant_broker_list_dicts(_handle,\r
143                                                  delegate(DictionaryInfo dictionary)\r
144                                                  { _dictionaries.Add(dictionary); });\r
145               VerifyNoErrors();\r
146           }\r
147         }\r
148 \r
149             #region IDisposable Members\r
150 \r
151         public void Dispose()\r
152                     {\r
153                             Dispose(true);\r
154                             GC.SuppressFinalize(this);\r
155                     }\r
156 \r
157                     #endregion\r
158 \r
159         private void Dispose(bool disposing)\r
160         {\r
161           if (!_disposed)\r
162           {\r
163             if (disposing)\r
164             {\r
165                 // dispose-only, i.e. non-finalizable logic\r
166                 if(_handle != null)\r
167                 {\r
168                     _handle.Dispose();\r
169                 }\r
170                 DisposeAllDictionariesFromCache(_dictionaryCache);\r
171                 DisposeAllDictionariesFromCache(_pwlDictionaryCache);\r
172             }\r
173 \r
174             // shared (dispose and finalizable) cleanup logic\r
175             _disposed = true;\r
176           }\r
177         }\r
178 \r
179             private static void DisposeAllDictionariesFromCache(ICollection<KeyValuePair<string, WeakReference>> cache) {\r
180                 List<Dictionary> dictionariesToDispose = new List<Dictionary>();\r
181             foreach (KeyValuePair<string, WeakReference> pair in cache)\r
182                 {\r
183                     if(pair.Value.IsAlive)\r
184                     {\r
185                         dictionariesToDispose.Add((Dictionary) pair.Value.Target);\r
186                     }\r
187                 }\r
188             cache.Clear();\r
189                 foreach (Dictionary dictionary in dictionariesToDispose)\r
190                 {\r
191                 dictionary.Dispose();\r
192                 }\r
193             }\r
194 \r
195             private void VerifyNotDisposed()\r
196         {\r
197           if (_disposed)\r
198           {\r
199             throw new ObjectDisposedException("Dictionary");\r
200           }\r
201         }\r
202 \r
203             public Dictionary RequestDictionary(string language_tag)\r
204             {\r
205                     VerifyNotDisposed();\r
206                     if (language_tag == null)\r
207                     {\r
208                             throw new ArgumentNullException("language_tag");\r
209                     }\r
210             if(_isLibEnchantAvailable != true)\r
211             {\r
212                 throw new InvalidOperationException("LibEnchant is not available");\r
213             }\r
214 \r
215             Dictionary dictionary = GetDictionaryFromCache(this._dictionaryCache, language_tag);\r
216             if(dictionary != null)\r
217             {\r
218                 return dictionary;\r
219             }\r
220                 return CreateDictionary(language_tag);\r
221             }\r
222 \r
223             private Dictionary CreateDictionary(string language_tag) {\r
224                 SafeDictionaryHandle handle = Bindings.enchant_broker_request_dict(this._handle, language_tag);\r
225                 VerifyNoErrors();\r
226                 if (handle.IsInvalid)\r
227                 {\r
228                     throw new ApplicationException("There is no provider that supplies a dictionary for " + language_tag);\r
229                 }\r
230 \r
231                 return CreateAndRegisterDictionary(handle, this._dictionaryCache, language_tag);\r
232             }\r
233 \r
234             private Dictionary GetDictionaryFromCache(IDictionary<string, WeakReference> cache, string language_tag) {\r
235                 if(CacheDictionaries)\r
236                 {\r
237                     WeakReference dictionaryReference;\r
238                     if (cache.TryGetValue(language_tag, out dictionaryReference))\r
239                     {\r
240                     if (dictionaryReference.IsAlive)\r
241                     {\r
242                         return (Dictionary) dictionaryReference.Target;\r
243                     }\r
244                     }\r
245                 }\r
246                 return null;\r
247             }\r
248 \r
249             private Dictionary CreateAndRegisterDictionary(SafeDictionaryHandle handle, IDictionary<string, WeakReference> cache, string language_tag) {\r
250                 Dictionary dictionary;\r
251                 dictionary = new Dictionary(handle);\r
252                 dictionary.Disposed += OnDictionaryDisposed;\r
253                 // always store the dictionaries we have created\r
254                 // so that we can dispose of them cleanly and give a \r
255                 // better error message (ObjectDisposed) instead of a crash\r
256                 // if someone tries to use a dictionary after the broker\r
257                 // that created it has been disposed.\r
258                 cache[language_tag] = new WeakReference(dictionary);\r
259                 return dictionary;\r
260             }\r
261 \r
262             private void OnDictionaryDisposed(object sender, EventArgs e)\r
263             {\r
264                 Dictionary dictionary = (Dictionary) sender;\r
265 \r
266             // try to remove from _dictionaryCache\r
267             if (!RemoveDictionaryFromCache(this._dictionaryCache, dictionary))\r
268             {\r
269                 // try to remove from _pwlDictionaryCache\r
270                 RemoveDictionaryFromCache(this._pwlDictionaryCache, dictionary);\r
271             }\r
272             }\r
273 \r
274             private static bool RemoveDictionaryFromCache(IDictionary<string, WeakReference> cache, Dictionary dictionary) {\r
275                 foreach (KeyValuePair<string, WeakReference> pair in cache)\r
276                 {\r
277                 if (pair.Value.IsAlive \r
278                     && pair.Value.Target == dictionary)\r
279                 {\r
280                     cache.Remove(pair.Key);\r
281                     return true;\r
282                 }\r
283                 }\r
284                 return false;\r
285             }\r
286 \r
287         public bool DictionaryExists(string language_tag)\r
288         {\r
289           VerifyNotDisposed();\r
290           if (language_tag == null)\r
291           {\r
292             throw new ArgumentNullException("language_tag");\r
293           }\r
294           if (_isLibEnchantAvailable != true)\r
295           {\r
296               return false;\r
297           }\r
298 \r
299           int result = Bindings.enchant_broker_dict_exists(_handle, language_tag);\r
300           VerifyNoErrors();\r
301           if (result != 0 && result != 1)\r
302           {\r
303             throw new NotImplementedException(\r
304               "enchant_broker_dict_exists returned unexpected value that is currently unhandled.");\r
305           }\r
306           return result == 1;\r
307         }\r
308 \r
309         public Dictionary RequestPwlDictionary(string pwlFile)\r
310                 {\r
311                         VerifyNotDisposed();\r
312                         if (pwlFile == null)\r
313                         {\r
314                                 throw new ArgumentNullException("pwlFile");\r
315                         }\r
316             if (_isLibEnchantAvailable != true)\r
317             {\r
318                 throw new InvalidOperationException("LibEnchant is not available");\r
319             }\r
320             Dictionary dictionary = GetDictionaryFromCache(this._pwlDictionaryCache, pwlFile);\r
321             if (dictionary != null)\r
322             {\r
323                 return dictionary;\r
324             }\r
325 \r
326                         return CreatePwlDictionary(pwlFile);\r
327                 }\r
328 \r
329             private Dictionary CreatePwlDictionary(string pwlFile) {\r
330                 SafeDictionaryHandle handle = Bindings.enchant_broker_request_pwl_dict(this._handle, pwlFile);\r
331                 VerifyNoErrors();\r
332                 if (handle.IsInvalid)\r
333                 {\r
334                     throw new ApplicationException("Unable to create pwl file " + pwlFile);\r
335                 }\r
336                 return CreateAndRegisterDictionary(handle, this._pwlDictionaryCache, pwlFile);\r
337             }\r
338 \r
339             public void SetOrdering(string language_tag, string ordering)\r
340                 {\r
341                         VerifyNotDisposed();\r
342             if (_isLibEnchantAvailable == true)\r
343             {\r
344                 Bindings.enchant_broker_set_ordering(_handle, language_tag, ordering);\r
345             }\r
346                 VerifyNoErrors();\r
347                 }\r
348 \r
349                 private void VerifyNoErrors()\r
350                 {\r
351             if (_isLibEnchantAvailable == true)\r
352             {\r
353                 string message = Bindings.enchant_broker_get_error(_handle);\r
354                 if (!string.IsNullOrEmpty(message))\r
355                 {\r
356                     throw new ApplicationException(message);\r
357                 }\r
358             }\r
359                 }\r
360         }\r
361 }\r