1 /* Copyright (c) 2007 Eric Scott Albright
\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
10 * The above copyright notice and this permission notice shall be included in
\r
11 * all copies or substantial portions of the Software.
\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
23 using System.Collections.Generic;
\r
27 public sealed class Broker : IDisposable
\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
38 private static Broker _defaultBroker;
\r
40 /// Singleton for apps that want to use that pattern
\r
42 public static Broker Default
\r
46 if(_defaultBroker == null || _defaultBroker._disposed)
\r
48 _defaultBroker = new Broker();
\r
50 return _defaultBroker;
\r
54 public static bool IsLibEnchantAvailable
\r
58 if (_isLibEnchantAvailable == null)
\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
64 return _isLibEnchantAvailable??false;
\r
73 _handle = Bindings.enchant_broker_init();
\r
74 _isLibEnchantAvailable = true;
\r
76 catch (DllNotFoundException)
\r
78 _isLibEnchantAvailable = false;
\r
80 if (_isLibEnchantAvailable == true)
\r
83 if (_handle.IsInvalid)
\r
85 throw new ApplicationException("Unable to initialize broker");
\r
88 _dictionaryCache = new Dictionary<string, WeakReference>();
\r
89 _pwlDictionaryCache = new Dictionary<string, WeakReference>();
\r
93 public IEnumerable<ProviderInfo> Providers
\r
97 VerifyNotDisposed();
\r
98 if (_providers == null)
\r
100 InitializeProviderList();
\r
106 private void InitializeProviderList()
\r
108 _providers = new List<ProviderInfo>();
\r
109 if (_isLibEnchantAvailable == true)
\r
111 Bindings.enchant_broker_describe(_handle,
\r
112 delegate(ProviderInfo provider)
\r
113 { _providers.Add(provider); });
\r
118 public IEnumerable<DictionaryInfo> Dictionaries
\r
122 VerifyNotDisposed();
\r
123 if (_dictionaries == null)
\r
125 InitializeDictionaryList();
\r
127 return _dictionaries;
\r
131 public bool CacheDictionaries
\r
133 get { return this._cacheDictionaries; }
\r
134 set { this._cacheDictionaries = value; }
\r
137 private void InitializeDictionaryList()
\r
139 _dictionaries = new List<DictionaryInfo>();
\r
140 if (_isLibEnchantAvailable == true)
\r
142 Bindings.enchant_broker_list_dicts(_handle,
\r
143 delegate(DictionaryInfo dictionary)
\r
144 { _dictionaries.Add(dictionary); });
\r
149 #region IDisposable Members
\r
151 public void Dispose()
\r
154 GC.SuppressFinalize(this);
\r
159 private void Dispose(bool disposing)
\r
165 // dispose-only, i.e. non-finalizable logic
\r
166 if(_handle != null)
\r
170 DisposeAllDictionariesFromCache(_dictionaryCache);
\r
171 DisposeAllDictionariesFromCache(_pwlDictionaryCache);
\r
174 // shared (dispose and finalizable) cleanup logic
\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
183 if(pair.Value.IsAlive)
\r
185 dictionariesToDispose.Add((Dictionary) pair.Value.Target);
\r
189 foreach (Dictionary dictionary in dictionariesToDispose)
\r
191 dictionary.Dispose();
\r
195 private void VerifyNotDisposed()
\r
199 throw new ObjectDisposedException("Dictionary");
\r
203 public Dictionary RequestDictionary(string language_tag)
\r
205 VerifyNotDisposed();
\r
206 if (language_tag == null)
\r
208 throw new ArgumentNullException("language_tag");
\r
210 if(_isLibEnchantAvailable != true)
\r
212 throw new InvalidOperationException("LibEnchant is not available");
\r
215 Dictionary dictionary = GetDictionaryFromCache(this._dictionaryCache, language_tag);
\r
216 if(dictionary != null)
\r
220 return CreateDictionary(language_tag);
\r
223 private Dictionary CreateDictionary(string language_tag) {
\r
224 SafeDictionaryHandle handle = Bindings.enchant_broker_request_dict(this._handle, language_tag);
\r
226 if (handle.IsInvalid)
\r
228 throw new ApplicationException("There is no provider that supplies a dictionary for " + language_tag);
\r
231 return CreateAndRegisterDictionary(handle, this._dictionaryCache, language_tag);
\r
234 private Dictionary GetDictionaryFromCache(IDictionary<string, WeakReference> cache, string language_tag) {
\r
235 if(CacheDictionaries)
\r
237 WeakReference dictionaryReference;
\r
238 if (cache.TryGetValue(language_tag, out dictionaryReference))
\r
240 if (dictionaryReference.IsAlive)
\r
242 return (Dictionary) dictionaryReference.Target;
\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
262 private void OnDictionaryDisposed(object sender, EventArgs e)
\r
264 Dictionary dictionary = (Dictionary) sender;
\r
266 // try to remove from _dictionaryCache
\r
267 if (!RemoveDictionaryFromCache(this._dictionaryCache, dictionary))
\r
269 // try to remove from _pwlDictionaryCache
\r
270 RemoveDictionaryFromCache(this._pwlDictionaryCache, dictionary);
\r
274 private static bool RemoveDictionaryFromCache(IDictionary<string, WeakReference> cache, Dictionary dictionary) {
\r
275 foreach (KeyValuePair<string, WeakReference> pair in cache)
\r
277 if (pair.Value.IsAlive
\r
278 && pair.Value.Target == dictionary)
\r
280 cache.Remove(pair.Key);
\r
287 public bool DictionaryExists(string language_tag)
\r
289 VerifyNotDisposed();
\r
290 if (language_tag == null)
\r
292 throw new ArgumentNullException("language_tag");
\r
294 if (_isLibEnchantAvailable != true)
\r
299 int result = Bindings.enchant_broker_dict_exists(_handle, language_tag);
\r
301 if (result != 0 && result != 1)
\r
303 throw new NotImplementedException(
\r
304 "enchant_broker_dict_exists returned unexpected value that is currently unhandled.");
\r
306 return result == 1;
\r
309 public Dictionary RequestPwlDictionary(string pwlFile)
\r
311 VerifyNotDisposed();
\r
312 if (pwlFile == null)
\r
314 throw new ArgumentNullException("pwlFile");
\r
316 if (_isLibEnchantAvailable != true)
\r
318 throw new InvalidOperationException("LibEnchant is not available");
\r
320 Dictionary dictionary = GetDictionaryFromCache(this._pwlDictionaryCache, pwlFile);
\r
321 if (dictionary != null)
\r
326 return CreatePwlDictionary(pwlFile);
\r
329 private Dictionary CreatePwlDictionary(string pwlFile) {
\r
330 SafeDictionaryHandle handle = Bindings.enchant_broker_request_pwl_dict(this._handle, pwlFile);
\r
332 if (handle.IsInvalid)
\r
334 throw new ApplicationException("Unable to create pwl file " + pwlFile);
\r
336 return CreateAndRegisterDictionary(handle, this._pwlDictionaryCache, pwlFile);
\r
339 public void SetOrdering(string language_tag, string ordering)
\r
341 VerifyNotDisposed();
\r
342 if (_isLibEnchantAvailable == true)
\r
344 Bindings.enchant_broker_set_ordering(_handle, language_tag, ordering);
\r
349 private void VerifyNoErrors()
\r
351 if (_isLibEnchantAvailable == true)
\r
353 string message = Bindings.enchant_broker_get_error(_handle);
\r
354 if (!string.IsNullOrEmpty(message))
\r
356 throw new ApplicationException(message);
\r