using System; using System.Reflection; namespace OpenTap { /// /// Marks a factory attribute, for now this can be internal. /// interface IFactoryAttribute { public string FactoryMethodName { get; } } /// Specifies that another member method can be used to create a value for a given property. public class FactoryAttribute : Attribute, IFactoryAttribute { /// The name of the method that can be used to create a value. This can be a private method. public string FactoryMethodName { get; } /// Creates an instance of FactoryAttribute. /// public FactoryAttribute(string factoryMethodName) { FactoryMethodName = factoryMethodName; } /// Creates an object by calling that target member. internal static object Create(object ownerObj, IFactoryAttribute factoryAttribute) { var type = ownerObj.GetType(); var fac = type.GetMethod(factoryAttribute.FactoryMethodName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); // This is a bug, but showing this message in the log is much nicer than a null reference exception. if (fac == null) throw new Exception( $"Factory method '{factoryAttribute.FactoryMethodName}' not found on object of type '{type.FullName}'"); return fac.Invoke(ownerObj, []); } // Recursively search for the source of the parameter to try constructing this object internal static bool TryCreateFromMember(IParameterMemberData member, IFactoryAttribute factoryAttribute, out object obj) { obj = null; if (member is IParameterMemberData pmd) { foreach (var p in pmd.ParameterizedMembers) { // If this is a concrete member, we should be able to use the source object as a factory source. if (p.Member is MemberData) { obj = Create(p.Source, factoryAttribute); return true; } // Otherwise, try recursing further if (p.Member is IParameterMemberData pmd2) { if (TryCreateFromMember(pmd2, factoryAttribute, out obj)) return true; } } } return false; } } /// Specifies that another member method can be used to create an element value for a given list property. public class ElementFactoryAttribute : Attribute, IFactoryAttribute { /// The name of the method that can be used to create a value. This can be a private method. public string FactoryMethodName { get; } /// Creates an instance of ElementFactoryAttribute. public ElementFactoryAttribute(string factoryMethodName) { FactoryMethodName = factoryMethodName; } } }