/* * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals. * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using QuantConnect.Logging; using QuantConnect.Packets; namespace QuantConnect.Parameters { /// /// Specifies a field or property is a parameter that can be set /// from an dictionary /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class ParameterAttribute : Attribute { /// /// Specifies the binding flags used by this implementation to resolve parameter attributes /// public const BindingFlags BindingFlags = System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Instance; /// /// Gets the name of this parameter /// public string Name { get; private set; } /// /// Initializes a new instance of the class /// /// The name of the parameter. If null is specified /// then the field or property name will be used public ParameterAttribute(string name = null) { Name = name; } /// /// Uses reflections to inspect the instance for any parameter attributes. /// If a value is found in the parameters dictionary, it is set. /// /// The parameters dictionary /// The instance to set parameters on public static void ApplyAttributes(Dictionary parameters, object instance) { if (instance == null) throw new ArgumentNullException(nameof(instance)); var type = instance.GetType(); // get all fields/properties on the instance var members = type.GetFields(BindingFlags).Concat(type.GetProperties(BindingFlags)); foreach (var memberInfo in members) { var fieldInfo = memberInfo as FieldInfo; var propertyInfo = memberInfo as PropertyInfo; // this line make static analysis a little happier, but should never actually throw if (fieldInfo == null && propertyInfo == null) { throw new InvalidOperationException("Resolved member that is neither FieldInfo or PropertyInfo"); } // check the member for our custom attribute var attribute = memberInfo.GetCustomAttribute(); if (attribute == null) continue; // if no name is specified in the attribute then use the member name var parameterName = attribute.Name ?? memberInfo.Name; // get the parameter string value to apply to the member string parameterValue; if (!parameters.TryGetValue(parameterName, out parameterValue)) continue; if (string.IsNullOrEmpty(parameterValue)) { Log.Error($"ParameterAttribute.ApplyAttributes(): parameter '{parameterName}' provided value is null/empty, skipping"); continue; } // if it's a read-only property with a parameter value we can't really do anything, bail if (propertyInfo != null && !propertyInfo.CanWrite) { var message = $"The specified property is read only: {propertyInfo.DeclaringType}.{propertyInfo.Name}"; throw new InvalidOperationException(message); } // resolve the member type var memberType = fieldInfo != null ? fieldInfo.FieldType : propertyInfo.PropertyType; // convert the parameter string value to the member type var value = parameterValue.ConvertTo(memberType); // set the value to the field/property if (fieldInfo != null) { fieldInfo.SetValue(instance, value); } else { propertyInfo.SetValue(instance, value); } } } /// /// Resolves all parameter attributes from the specified compiled assembly path /// /// The assembly to inspect /// Parameters dictionary keyed by parameter name with a value of the member type public static Dictionary GetParametersFromAssembly(Assembly assembly) { var parameters = new Dictionary(); foreach (var type in assembly.GetTypes()) { foreach (var kvp in GetParametersFromType(type)) { parameters[kvp.Key] = kvp.Value; } } return parameters; } /// /// Resolves all parameter attributes from the specified type /// /// The type to inspect /// Parameters dictionary keyed by parameter name with a value of the member type public static IEnumerable> GetParametersFromType(Type type) { foreach (var field in type.GetFields(BindingFlags)) { var attribute = field.GetCustomAttribute(); if (attribute != null) { var parameterName = attribute.Name ?? field.Name; yield return new KeyValuePair(parameterName, field.FieldType.GetBetterTypeName()); } } foreach (var property in type.GetProperties(BindingFlags)) { // ignore non-writeable properties if (!property.CanWrite) continue; var attribute = property.GetCustomAttribute(); if (attribute != null) { var parameterName = attribute.Name ?? property.Name; yield return new KeyValuePair(parameterName, property.PropertyType.GetBetterTypeName()); } } } } }