public virtual object GetKey(ICUServiceKey key, IServiceFactory factory, out string actualReturn) { if (factories.Count == 0) { return(HandleDefault(key, out actualReturn)); } var result = GetKeyCacheEntry(key, factory); if (result != null) { // strip null prefix if (result.actualDescriptor.IndexOf('/') == 0) { actualReturn = result.actualDescriptor.Substring(1); } else { actualReturn = result.actualDescriptor; } return(result.service); } return(HandleDefault(key, out actualReturn)); }
/// <summary> /// Return the service instance if the factory's id is equal to /// the <paramref name="key"/>'s <see cref="ICUServiceKey.CurrentID"/>. /// <paramref name="service"/> is ignored. /// </summary> public virtual object Create(ICUServiceKey key, ICUService service) { if (id.Equals(key.CurrentID)) { return(instance); } return(null); }
/// <summary> /// Return a snapshot of the mapping from display names to visible /// IDs for this service. This set will not change as factories /// are added or removed, but the supported ids will, so there is /// no guarantee that all and only the ids in the returned map will /// be visible and supported by the service in subsequent calls, /// nor is there any guarantee that the current display names match /// those in the set. The display names are sorted based on the /// comparer provided. /// </summary> public virtual IDictionary <string, string> GetDisplayNames(UCultureInfo locale, IComparer <string> com, string matchID) { IDictionary <string, string> dncache = null; LocaleRef reference = dnref; if (reference != null) { dncache = reference.Get(locale, com); } while (dncache == null) { lock (syncLock) { if (reference == dnref || dnref == null) { dncache = new SortedDictionary <string, string>(com); // sorted foreach (var e in GetVisibleIDMap()) { string id = e.Key; IServiceFactory f = e.Value; dncache[f.GetDisplayName(id, locale)] = id; } dncache = dncache.AsReadOnly(); dnref = new LocaleRef(dncache, locale, com); } else { reference = dnref; dncache = reference.Get(locale, com); } } } ICUServiceKey matchKey = CreateKey(matchID); if (matchKey == null) { return(dncache.AsReadOnly()); } // ICU4N: Rather than copying and then removing the items (which isn't allowed with // .NET iterators), we reverse the logic and add the items only if they are fallback. IDictionary <string, string> result = new SortedDictionary <string, string>(com); foreach (var e in dncache) { if (matchKey.IsFallbackOf(e.Value)) { result.Add(e.Key, e.Value); } } return(result); }
protected virtual bool HandlesKey(ICUServiceKey key) { if (key != null) { string id = key.CurrentID; ICollection <string> supported = GetSupportedIDs(); return(supported.Contains(id)); } return(false); }
// debugging // Map hardRef; public virtual object GetKey(ICUServiceKey key, IServiceFactory factory) { if (factories.Count == 0) { return(HandleDefault(key, out _)); } var result = GetKeyCacheEntry(key, factory); return((result != null) ? result.service : HandleDefault(key, out _)); }
/// <summary> /// Implement superclass abstract method. This checks the <see cref="ICUServiceKey.CurrentID"/> /// against the supported IDs, and passes the canonicalLocale and /// <see cref="LocaleKey.Kind"/> to <see cref="HandleCreate(UCultureInfo, int, ICUService)"/> (which subclasses must implement). /// </summary> public virtual object Create(ICUServiceKey key, ICUService service) { if (HandlesKey(key)) { LocaleKey lkey = (LocaleKey)key; int kind = lkey.Kind; UCultureInfo uloc = lkey.GetCurrentCulture(); return(HandleCreate(uloc, kind, service)); } else { // System.out.println("factory: " + this + " did not support id: " + key.currentID()); // System.out.println("supported ids: " + getSupportedIDs()); } return(null); }
/// <summary> /// Convenience override for callers using locales. This uses /// <see cref="CreateKey(string, int)"/> to create a key, calls /// <see cref="ICUService.GetKey(ICUServiceKey)"/>, and then /// returns the <paramref name="actualResult"/> from /// <see cref="ICUService.GetKey(ICUServiceKey)"/> (stripping any prefix) /// into a <see cref="UCultureInfo"/>. /// </summary> /// <param name="locale"></param> /// <param name="kind"></param> /// <param name="actualResult"></param> /// <returns></returns> public virtual object Get(UCultureInfo locale, int kind, out UCultureInfo actualResult) { actualResult = null; ICUServiceKey key = CreateKey(locale, kind); object result = GetKey(key, out string temp); if (result != null) { int n = temp.IndexOf('/'); if (n >= 0) { temp = temp.Substring(n + 1); } actualResult = new UCultureInfo(temp); } return(result); }
/// <summary> /// Returns the service object if kind/locale match. Service is not used. /// </summary> public override object Create(ICUServiceKey key, ICUService service) { if (!(key is LocaleKey)) { return(null); } LocaleKey lkey = (LocaleKey)key; if (kind != LocaleKey.KindAny && kind != lkey.Kind) { return(null); } if (!id.Equals(lkey.CurrentID)) { return(null); } return(obj); }
/// <summary> /// Return a snapshot of the visible IDs for this service. This /// set will not change as Factories are added or removed, but the /// supported ids will, so there is no guarantee that all and only /// the ids in the returned set are visible and supported by the /// service in subsequent calls. /// <para/> /// <paramref name="matchID"/> is passed to <see cref="CreateKey(string)"/> to create a key. If the /// key is not null, it is used to filter out ids that don't have /// the key as a fallback. /// </summary> public virtual ICollection <string> GetVisibleIDs(string matchID) // ICU4N specific - changed return type from ISet to ICollection to avoid O(n) { ICollection <string> result = GetVisibleIDMap().Keys; ICUServiceKey fallbackKey = CreateKey(matchID); if (fallbackKey != null) { ISet <string> temp = new HashSet <string>(/*result.Count*/); foreach (string id in result) { if (fallbackKey.IsFallbackOf(id)) { temp.Add(id); } } result = temp; } return(result); }
/// <summary> /// Given a visible <paramref name="id"/>, return the display name in the requested <paramref name="locale"/>. /// If there is no directly supported id corresponding to this id, return /// null. /// </summary> public virtual string GetDisplayName(string id, UCultureInfo locale) { IDictionary <string, IServiceFactory> m = GetVisibleIDMap(); if (m.TryGetValue(id, out IServiceFactory f) && f != null) { return(f.GetDisplayName(id, locale)); } ICUServiceKey key = CreateKey(id); while (key.Fallback()) { if (m.TryGetValue(key.CurrentID, out f) && f != null) { return(f.GetDisplayName(id, locale)); } } return(null); }
/// <summary> /// Return a snapshot of the visible IDs for this service. This /// set will not change as Factories are added or removed, but the /// supported ids will, so there is no guarantee that all and only /// the ids in the returned set are visible and supported by the /// service in subsequent calls. /// <para/> /// <paramref name="matchID"/> is passed to <see cref="CreateKey(string)"/> to create a key. If the /// key is not null, it is used to filter out ids that don't have /// the key as a fallback. /// </summary> public virtual ICollection <string> GetVisibleIDs(string matchID) // ICU4N specific - changed return type from ISet to ICollection to avoid O(n) { ICollection <string> result = GetVisibleIDMap().Keys; ICUServiceKey fallbackKey = CreateKey(matchID); if (fallbackKey != null) { // ICU4N: We don't need the overhead of HashSet here, since we are pulling // keys from a dictionary. var temp = new List <string>(result.Count); foreach (string id in result) { if (fallbackKey.IsFallbackOf(id)) { temp.Add(id); } } result = temp; } return(result); }
/// <summary> /// Given a visible <paramref name="id"/>, return the display name in the requested <paramref name="locale"/>. /// If there is no directly supported id corresponding to this id, return /// null. /// </summary> public virtual string GetDisplayName(string id, ULocale locale) { IDictionary <string, IServiceFactory> m = GetVisibleIDMap(); IServiceFactory f = m.Get(id); if (f != null) { return(f.GetDisplayName(id, locale)); } ICUServiceKey key = CreateKey(id); while (key.Fallback()) { f = m.Get(key.CurrentID); if (f != null) { return(f.GetDisplayName(id, locale)); } } return(null); }
/// <summary> /// Convenience override for callers using locales. This uses /// <see cref="CreateKey(string, int)"/> to create a key, calls /// <see cref="ICUService.GetKey(ICUServiceKey)"/>, and then /// if <paramref name="actualReturn"/> is not null, returns the actualResult from /// <see cref="ICUService.GetKey(ICUServiceKey)"/> (stripping any prefix) into a <see cref="ULocale"/>. /// </summary> /// <param name="locale"></param> /// <param name="kind"></param> /// <param name="actualReturn"></param> /// <returns></returns> public virtual object Get(ULocale locale, int kind, ULocale[] actualReturn) { ICUServiceKey key = CreateKey(locale, kind); if (actualReturn == null) { return(GetKey(key)); } string[] temp = new string[1]; object result = GetKey(key, temp); if (result != null) { int n = temp[0].IndexOf('/'); if (n >= 0) { temp[0] = temp[0].Substring(n + 1); } actualReturn[0] = new ULocale(temp[0]); } return(result); }
/// <summary> /// Convenience override for callers using locales. This uses /// <see cref="CreateKey(string, int)"/> to create a key, and returns the result of /// <see cref="ICUService.GetKey(ICUServiceKey)"/>. /// </summary> /// <param name="locale"></param> /// <param name="kind"></param> /// <returns></returns> public virtual object Get(UCultureInfo locale, int kind) { ICUServiceKey key = CreateKey(locale, kind); return(GetKey(key)); }
/// <summary> /// Convenience override for <see cref="GetKey(ICUServiceKey, out string)"/>. /// </summary> public virtual object GetKey(ICUServiceKey key) { return(GetKey(key, out _)); }
/// <summary> /// Given a <paramref name="key"/>, return a service object, and, if <paramref name="actualReturn"/> /// is not null, the descriptor with which it was found in the /// first element of <paramref name="actualReturn"/>. If no service object matches /// this key, return null, and leave <paramref name="actualReturn"/> unchanged. /// </summary> /// <remarks> /// This queries the cache using the <paramref name="key"/>'s descriptor, and if no /// object in the cache matches it, tries the <paramref name="key"/> on each /// registered factory, in order. If none generates a service /// object for the key, repeats the process with each fallback of /// the <paramref name="key"/>, until either one returns a service object, or the <paramref name="key"/> /// has no fallback. /// <para/> /// If <paramref name="key"/> is null, just returns null. /// </remarks> public virtual object GetKey(ICUServiceKey key, string[] actualReturn) { return(GetKey(key, actualReturn, null)); }
/// <summary> /// Default handler for this service if no factory in the list /// handled the <paramref name="key"/>. /// </summary> protected virtual object HandleDefault(ICUServiceKey key, out string actualIDReturn) { actualIDReturn = null; return(null); }
private CacheEntry GetKeyCacheEntry(ICUServiceKey key, IServiceFactory factory) { if (DEBUG) { Console.Out.WriteLine("Service: " + m_name + " key: " + key.CanonicalID); } if (key != null) { try { // The factory list can't be modified until we're done, // otherwise we might update the cache with an invalid result. // The cache has to stay in synch with the factory list. factoryLock.AcquireRead(); IDictionary <string, CacheEntry> cache = this.cache; // copy so we don't need to sync on this if (cache == null) { if (DEBUG) { Console.Out.WriteLine("Service " + m_name + " cache was empty"); } // synchronized since additions and queries on the cache must be atomic // they can be interleaved, though cache = new ConcurrentDictionary <string, CacheEntry>(); } string currentDescriptor = null; List <string> cacheDescriptorList = null; bool putInCache = false; int NDebug = 0; int startIndex = 0; int limit = factories.Count; bool cacheResult = true; if (factory != null) { for (int i = 0; i < limit; ++i) { if (factory == factories[i]) { startIndex = i + 1; break; } } if (startIndex == 0) { throw new InvalidOperationException("Factory " + factory + "not registered with service: " + this); } cacheResult = false; } CacheEntry result; //outer: do { currentDescriptor = key.GetCurrentDescriptor(); if (DEBUG) { Console.Out.WriteLine(m_name + "[" + NDebug++ + "] looking for: " + currentDescriptor); } if (cache.TryGetValue(currentDescriptor, out result) && result != null) { if (DEBUG) { Console.Out.WriteLine(m_name + " found with descriptor: " + currentDescriptor); } goto outer_break; } else { if (DEBUG) { Console.Out.WriteLine("did not find: " + currentDescriptor + " in cache"); } } // first test of cache failed, so we'll have to update // the cache if we eventually succeed-- that is, if we're // going to update the cache at all. putInCache = cacheResult; // int n = 0; int index = startIndex; while (index < limit) { IServiceFactory f = factories[index++]; if (DEBUG) { Console.Out.WriteLine("trying factory[" + (index - 1) + "] " + f.ToString()); } object service = f.Create(key, this); if (service != null) { result = new CacheEntry(currentDescriptor, service); if (DEBUG) { Console.Out.WriteLine(m_name + " factory supported: " + currentDescriptor + ", caching"); } goto outer_break; } else { if (DEBUG) { Console.Out.WriteLine("factory did not support: " + currentDescriptor); } } } // prepare to load the cache with all additional ids that // will resolve to result, assuming we'll succeed. We // don't want to keep querying on an id that's going to // fallback to the one that succeeded, we want to hit the // cache the first time next goaround. if (cacheDescriptorList == null) { cacheDescriptorList = new List <string>(5); } cacheDescriptorList.Add(currentDescriptor); } while (key.Fallback()); outer_break : { } if (result != null) { if (putInCache) { if (DEBUG) { Console.Out.WriteLine("caching '" + result.actualDescriptor + "'"); } cache[result.actualDescriptor] = result; if (cacheDescriptorList != null) { foreach (string desc in cacheDescriptorList) { if (DEBUG) { Console.Out.WriteLine(m_name + " adding descriptor: '" + desc + "' for actual: '" + result.actualDescriptor + "'"); } cache[desc] = result; } } // Atomic update. We held the read lock all this time // so we know our cache is consistent with the factory list. // We might stomp over a cache that some other thread // rebuilt, but that's the breaks. They're both good. this.cache = cache; } if (DEBUG) { Console.Out.WriteLine("found in service: " + m_name); } return(result); } } finally { factoryLock.ReleaseRead(); } } if (DEBUG) { Console.Out.WriteLine("not found in service: " + m_name); } return(null); }
/// <summary> /// Default handler for this service if no factory in the list /// handled the <paramref name="key"/>. /// </summary> protected virtual object HandleDefault(ICUServiceKey key, string[] actualIDReturn) { return(null); }
/// <summary> /// Given a <paramref name="key"/>, return a service object, and the descriptor with which /// it was found in the <paramref name="actualReturn"/>. If no service object matches /// this key, return <c>null</c>, and leave <paramref name="actualReturn"/> <c>null</c>. /// </summary> /// <remarks> /// This queries the cache using the <paramref name="key"/>'s descriptor, and if no /// object in the cache matches it, tries the <paramref name="key"/> on each /// registered factory, in order. If none generates a service /// object for the <paramref name="key"/>, repeats the process with each fallback of /// the <paramref name="key"/>, until either one returns a service object, or the <paramref name="key"/> /// has no fallback. /// <para/> /// If <paramref name="key"/> is <c>null</c>, just returns <c>null</c>. /// </remarks> public virtual object GetKey(ICUServiceKey key, out string actualReturn) { return(GetKey(key, null, out actualReturn)); }