using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using Chernobyl.Attribution; using Chernobyl.Event; using Chernobyl.Reflection; using Chernobyl.Values; namespace Chernobyl.Dependency { /// /// An that retrieves the value of a parameter that /// is attributed with an . If the parameter is /// attributed with an whose /// key is an and whose value is an /// holding an object, then that instance will be used to retrieve the parameter. /// Otherwise, the parameter will be taken from an . /// [AttributeUsage(AttributeTargets.Parameter)] public class GetAttribute : Attribute, IGetAttribute { /// /// Returns the value of the specified attribute provider. The attribute /// provider must be the instance that this /// was attributing. /// /// The instance this /// was /// attributing. /// /// The value of the requested item. /// public IValue Get(ICustomAttributeProvider attributeProvider) { var parameterInfo = attributeProvider.Require(); // Grab the storage containers for this parameter. var storages = attributeProvider .GetCustomAttributes>>(true) .DefaultIfEmpty(StaticStorageAttribute.Default).ToArray(); if (storages.Length > 1) { Type storageType = typeof (IStorageAttribute>); throw new ParameterException("The attribute provider \"" + attributeProvider + "\" is attributed with more than one " + storageType + " instance. " + typeof(GetAttribute) + " does not currently support more than " + "one storage instance. Please ensure that all instances " + "are attributed with only one " + storageType, parameterInfo.ToEnumerable()); } // Grab the key for this parameter. var storage = storages.First(); var keyAttributes = attributeProvider .GetCustomAttributes(true) .ToArray(); if(keyAttributes.Length == 0) throw new ParameterException("Unable to retrieve the parameter \"" + attributeProvider + "\" as it is not " + "attributed with a \"" + typeof(IKeyAttribute) + "\".", parameterInfo.ToEnumerable()); if (keyAttributes.Length > 1) throw new ParameterException("Unable to retrieve the parameter \"" + attributeProvider + "\" as it is attributed " + "with more than one \"" + typeof(IKeyAttribute) + "\". GetAttribute cannot choose between the keys.", parameterInfo.ToEnumerable()); var keyAttribute = keyAttributes.First(); // Grab the IValue parameter from the store. IValue parameter; if (storage.Container.TryGetValue(keyAttribute.Key, out parameter) == false) { // The parameter was not in the Store so we'll wait for it // to be added. var sourceParameter = new SourceValue(); EventHandler>>> itemsAdded = null; itemsAdded = (sender, e) => { var keyValues = e.Items.Where(pair => pair.Key.Equals(keyAttribute.Key)); if (keyValues.Any()) { sourceParameter.Source = keyValues.First().Value; // The item has been added, we no longer need to // listen for addition of items. storage.Container.ItemsAdded -= itemsAdded; // We don't worry about the items being remove from // the container for now. In the future we may want // to add functionality to support removal and re-adding. } }; storage.Container.ItemsAdded += itemsAdded; parameter = sourceParameter; } return parameter; } /// /// Returns the value of the specified attribute provider. The attribute /// provider must be the instance that this /// was attributing. /// /// The instance this /// was /// attributing. /// The value of the requested item. public IValue Get(ParameterInfo parameterInfo) { return Get((ICustomAttributeProvider)parameterInfo); } } }