/* * Copyright (c) 2006-2014, openmetaverse.org * All rights reserved. * * - Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * - Neither the name of the openmetaverse.org nor the names * of its contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ using System; using System.Collections.Generic; using System.Collections; namespace OpenMetaverse { /// /// /// public enum DictionaryEventAction { /// /// /// Add, /// /// /// Remove, /// /// /// Change } /// /// /// /// /// public delegate void DictionaryChangeCallback(DictionaryEventAction action, DictionaryEntry entry); /// /// The ObservableDictionary class is used for storing key/value pairs. It has methods for firing /// events to subscribers when items are added, removed, or changed. /// /// Key /// Value public class ObservableDictionary { #region Observable implementation /// /// A dictionary of callbacks to fire when specified action occurs /// private Dictionary> Delegates; /// /// Register a callback to be fired when an action occurs /// /// The action /// The callback to fire public void AddDelegate(DictionaryEventAction action, DictionaryChangeCallback callback) { if (Delegates.ContainsKey(action)) { Delegates[action].Add(callback); } else { List callbacks = new List(1); callbacks.Add(callback); Delegates.Add(action, callbacks); } } /// /// Unregister a callback /// /// The action /// The callback to fire public void RemoveDelegate(DictionaryEventAction action, DictionaryChangeCallback callback) { if (Delegates.ContainsKey(action)) { if (Delegates[action].Contains(callback)) Delegates[action].Remove(callback); } } /// /// /// /// /// private void FireChangeEvent(DictionaryEventAction action, DictionaryEntry entry) { if(Delegates.ContainsKey(action)) { foreach(DictionaryChangeCallback handler in Delegates[action]) { handler(action, entry); } } } #endregion /// Internal dictionary that this class wraps around. Do not /// modify or enumerate the contents of this dictionary without locking private Dictionary Dictionary; /// /// Gets the number of Key/Value pairs contained in the /// public int Count { get { return Dictionary.Count; } } /// /// Initializes a new instance of the Class /// with the specified key/value, has the default initial capacity. /// /// /// /// // initialize a new ObservableDictionary named testDict with a string as the key and an int as the value. /// public ObservableDictionary<string, int> testDict = new ObservableDictionary<string, int>(); /// /// public ObservableDictionary() { Dictionary = new Dictionary(); Delegates = new Dictionary>(); } /// /// Initializes a new instance of the Class /// with the specified key/value, With its initial capacity specified. /// /// Initial size of dictionary /// /// /// // initialize a new ObservableDictionary named testDict with a string as the key and an int as the value, /// // initially allocated room for 10 entries. /// public ObservableDictionary<string, int> testDict = new ObservableDictionary<string, int>(10); /// /// public ObservableDictionary(int capacity) { Dictionary = new Dictionary(capacity); Delegates = new Dictionary>(); } /// /// Try to get entry from the with specified key /// /// Key to use for lookup /// Value returned /// if specified key exists, if not found /// /// /// // find your avatar using the Simulator.ObjectsAvatars ObservableDictionary: /// Avatar av; /// if (Client.Network.CurrentSim.ObjectsAvatars.TryGetValue(Client.Self.AgentID, out av)) /// Console.WriteLine("Found Avatar {0}", av.Name); /// /// /// public bool TryGetValue(TKey key, out TValue value) { return Dictionary.TryGetValue(key, out value); } /// /// Finds the specified match. /// /// The match. /// Matched value /// /// /// // use a delegate to find a prim in the ObjectsPrimitives ObservableDictionary /// // with the ID 95683496 /// uint findID = 95683496; /// Primitive findPrim = sim.ObjectsPrimitives.Find( /// delegate(Primitive prim) { return prim.ID == findID; }); /// /// public TValue Find(Predicate match) { foreach (TValue value in Dictionary.Values) { if (match(value)) return value; } return default(TValue); } /// Find All items in an /// return matching items. /// a containing found items. /// /// Find All prims within 20 meters and store them in a List /// /// int radius = 20; /// List<Primitive> prims = Client.Network.CurrentSim.ObjectsPrimitives.FindAll( /// delegate(Primitive prim) { /// Vector3 pos = prim.Position; /// return ((prim.ParentID == 0) && (pos != Vector3.Zero) && (Vector3.Distance(pos, location) < radius)); /// } /// ); /// /// public List FindAll(Predicate match) { List found = new List(); foreach (KeyValuePair kvp in Dictionary) { if (match(kvp.Value)) found.Add(kvp.Value); } return found; } /// Find All items in an /// return matching keys. /// a containing found keys. /// /// Find All keys which also exist in another dictionary /// /// List<UUID> matches = myDict.FindAll( /// delegate(UUID id) { /// return myOtherDict.ContainsKey(id); /// } /// ); /// /// public List FindAll(Predicate match) { List found = new List(); foreach (KeyValuePair kvp in Dictionary) { if (match(kvp.Key)) found.Add(kvp.Key); } return found; } /// Check if Key exists in Dictionary /// Key to check for /// if found, otherwise public bool ContainsKey(TKey key) { return Dictionary.ContainsKey(key); } /// Check if Value exists in Dictionary /// Value to check for /// if found, otherwise public bool ContainsValue(TValue value) { return Dictionary.ContainsValue(value); } /// /// Adds the specified key to the dictionary, dictionary locking is not performed, /// /// /// The key /// The value public void Add(TKey key, TValue value) { Dictionary.Add(key, value); FireChangeEvent(DictionaryEventAction.Add, new DictionaryEntry(key, value)); } /// /// Removes the specified key, dictionary locking is not performed /// /// The key. /// if successful, otherwise public bool Remove(TKey key) { FireChangeEvent(DictionaryEventAction.Remove, new DictionaryEntry(key, Dictionary[key])); return Dictionary.Remove(key); } /// /// Indexer for the dictionary /// /// The key /// The value public TValue this[TKey key] { get { return Dictionary[key]; } set { FireChangeEvent(DictionaryEventAction.Add, new DictionaryEntry(key, value)); Dictionary[key] = value; } } /// /// Clear the contents of the dictionary /// public void Clear() { foreach (KeyValuePair kvp in Dictionary) FireChangeEvent(DictionaryEventAction.Remove, new DictionaryEntry(kvp.Key, kvp.Value)); Dictionary.Clear(); } /// /// Enumerator for iterating dictionary entries /// /// public System.Collections.IEnumerator GetEnumerator() { return Dictionary.GetEnumerator(); } } }