/// <summary> /// Return all fields. The normal .NET reflection doesn't return private fields in base classes. /// This function does. /// </summary> public static List <IVariable> GetAllDeclarations(object obj, Type type, BindingFlags flags) { if (type == typeof(Object) || type == typeof(Model)) { return(new List <IVariable>()); } var list = GetAllDeclarations(obj, type.BaseType, flags); // in order to avoid duplicates, force BindingFlags.DeclaredOnly foreach (FieldInfo field in type.GetFields(flags | BindingFlags.DeclaredOnly)) { foreach (Attribute a in field.GetCustomAttributes()) { LinkAttribute link = a as LinkAttribute; if (a != null) { list.Add(new VariableField(obj, field)); } } } foreach (PropertyInfo property in type.GetProperties(flags | BindingFlags.DeclaredOnly)) { foreach (Attribute a in property.GetCustomAttributes()) { LinkAttribute link = a as LinkAttribute; if (a != null) { list.Add(new VariableProperty(obj, property)); } } } return(list); }
/// <summary> /// Set to null all link fields in the specified model. /// </summary> /// <param name="model">The model to look through for links</param> public static void UnresolveLinks(IModel model) { // Go looking for private [Link]s foreach (FieldInfo field in ReflectionUtilities.GetAllFields( model.GetType(), BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public)) { LinkAttribute link = ReflectionUtilities.GetAttribute(field, typeof(LinkAttribute), false) as LinkAttribute; if (link != null) { field.SetValue(model, null); } } }
/// <summary> /// Go looking for a link attribute. /// </summary> /// <param name="field">The associated field.</param> /// <returns>Returns link or null if none field on specified field.</returns> private static LinkAttribute GetLinkAttribute(FieldInfo field) { var attributes = field.GetCustomAttributes(); foreach (Attribute attribute in attributes) { LinkAttribute link = attribute as LinkAttribute; if (link != null) { return(link); } } return(null); }
/// <summary> /// Set to null all link fields in the specified model. /// </summary> /// <param name="model">The model to look through for links</param> public void Unresolve(IModel model) { foreach (IModel modelNode in Apsim.ChildrenRecursively(model)) { // Go looking for private [Link]s foreach (IVariable declaration in GetAllDeclarations(model, model.GetType(), BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public)) { LinkAttribute link = declaration.GetAttribute(typeof(LinkAttribute)) as LinkAttribute; if (link != null) { declaration.Value = null; } } } }
/// <summary> /// Resolve links in an unknown object e.g. user interface presenter /// </summary> /// <param name="obj"></param> public void Resolve(object obj) { // Go looking for [Link]s foreach (IVariable field in GetAllDeclarations(obj, GetModel(obj).GetType(), BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public)) { LinkAttribute link = field.GetAttribute(typeof(LinkAttribute)) as LinkAttribute; if (link != null) { // For now only try matching on a service object match = services.Find(s => field.DataType.IsAssignableFrom(s.GetType())); if (match != null) { field.Value = GetModel(match); } else if (!link.IsOptional) { throw new Exception("Cannot find a match for link " + field.Name + " in model " + GetFullName(obj)); } } } }
/// <summary> /// Set to null all link fields in the specified model. /// </summary> /// <param name="model">The model to look through for links</param> /// <param name="allLinks">Unresolve all links or just the non child links?</param> public void Unresolve(IModel model, bool allLinks) { List <IModel> allModels = new List <IModel>() { model }; allModels.AddRange(Apsim.ChildrenRecursively(model)); foreach (IModel modelNode in allModels) { // Go looking for private [Link]s foreach (IVariable declaration in GetAllDeclarations(modelNode, modelNode.GetType(), BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public, allLinks)) { LinkAttribute link = declaration.GetAttribute(typeof(LinkAttribute)) as LinkAttribute; if (link != null) { declaration.Value = null; } } } }
/// <summary> /// Resolve links in an unknown object e.g. user interface presenter /// </summary> /// <param name="obj"></param> public void Resolve(object obj) { // Go looking for [Link]s foreach (FieldInfo field in ReflectionUtilities.GetAllFields( GetModel(obj).GetType(), BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public)) { LinkAttribute link = GetLinkAttribute(field); if (link != null) { // For now only try matching on a service object match = services.Find(s => field.FieldType.IsAssignableFrom(s.GetType())); if (match != null) { field.SetValue(GetModel(obj), GetModel(match)); } else if (!link.IsOptional) { throw new Exception("Cannot find a match for link " + field.Name + " in model " + GetFullName(obj)); } } } }
/// <summary> /// Internal [link] resolution algorithm. /// </summary> /// <param name="obj"></param> /// <param name="scope">The scoping rules to use to resolve links.</param> private void ResolveInternal(object obj, ScopingRules scope) { foreach (IVariable field in GetAllDeclarations(GetModel(obj), GetModel(obj).GetType(), BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public, allLinks: true)) { LinkAttribute link = field.GetAttribute(typeof(LinkAttribute)) as LinkAttribute; if (link != null) { Type fieldType = field.DataType; if (fieldType.IsArray) { fieldType = fieldType.GetElementType(); } else if (field.DataType.Name.StartsWith("List") && field.DataType.GenericTypeArguments.Length == 1) { fieldType = field.DataType.GenericTypeArguments[0]; } List <object> matches; matches = services.FindAll(s => fieldType.IsAssignableFrom(s.GetType())); if (matches.Count == 0 && obj is IModel) { Simulation parentSimulation = Apsim.Parent(obj as IModel, typeof(Simulation)) as Simulation; if (fieldType.IsAssignableFrom(typeof(ILocator)) && parentSimulation != null) { matches.Add(new Locator(obj as IModel)); } else if (fieldType.IsAssignableFrom(typeof(IEvent)) && parentSimulation != null) { matches.Add(new Events(obj as IModel)); } } if (matches.Count == 0) { if (link is ParentLinkAttribute) { matches = new List <object>(); matches.Add(GetParent(obj, fieldType)); } else if (link is LinkByPathAttribute) { var locater = new Locater(); object match = locater.Get((link as LinkByPathAttribute).Path, obj as Model); if (match != null) { matches.Add(match); } } else if (link.IsScoped(field)) { matches = scope.FindAll(obj as IModel).Cast <object>().ToList(); } else { matches = GetChildren(obj); } } matches.RemoveAll(match => !fieldType.IsAssignableFrom(GetModel(match).GetType())); if (link.UseNameToMatch(field)) { matches.RemoveAll(match => !StringUtilities.StringsAreEqual(GetName(match), field.Name)); } if (field.DataType.IsArray) { Array array = Array.CreateInstance(fieldType, matches.Count); for (int i = 0; i < matches.Count; i++) { array.SetValue(GetModel(matches[i]), i); } field.Value = array; } else if (field.DataType.Name.StartsWith("List") && field.DataType.GenericTypeArguments.Length == 1) { var listType = typeof(List <>); var constructedListType = listType.MakeGenericType(fieldType); IList array = Activator.CreateInstance(constructedListType) as IList; for (int i = 0; i < matches.Count; i++) { array.Add(GetModel(matches[i])); } field.Value = array; } else if (matches.Count == 0) { if (!link.IsOptional) { throw new Exception("Cannot find a match for link " + field.Name + " in model " + GetFullName(obj)); } } else if (matches.Count >= 2 && !link.IsScoped(field)) { throw new Exception(string.Format(": Found {0} matches for link {1} in model {2} !", matches.Count, field.Name, GetFullName(obj))); } else { field.Value = GetModel(matches[0]); } } } }
/// <summary> /// Internal [link] resolution algorithm. /// </summary> /// <param name="obj"></param> /// <param name="allModels">A collection of all model wrappers</param> private void ResolveInternal(object obj, List <ModelWrapper> allModels) { // Go looking for [Link]s foreach (FieldInfo field in ReflectionUtilities.GetAllFields( GetModel(obj).GetType(), BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public)) { LinkAttribute link = GetLinkAttribute(field); if (link != null) { // Get the field type or the array element if it is an array field. Type fieldType = field.FieldType; if (fieldType.IsArray) { fieldType = fieldType.GetElementType(); } else if (field.FieldType.Name.StartsWith("List") && field.FieldType.GenericTypeArguments.Length == 1) { fieldType = field.FieldType.GenericTypeArguments[0]; } // Try and get a match from our services first. List <object> matches; matches = services.FindAll(s => fieldType.IsAssignableFrom(s.GetType())); // If no match on services then try other options. if (matches.Count == 0 && obj is IModel) { Simulation parentSimulation = Apsim.Parent(obj as IModel, typeof(Simulation)) as Simulation; if (fieldType.IsAssignableFrom(typeof(ILocator)) && parentSimulation != null) { matches.Add(new Locator(obj as IModel)); } else if (fieldType.IsAssignableFrom(typeof(IEvent)) && parentSimulation != null) { matches.Add(new Events(obj as IModel)); } } // If no match on services then try other options. if (matches.Count == 0) { // Get a list of models that could possibly match. if (link is ParentLinkAttribute) { matches = new List <object>(); matches.Add(GetParent(obj, fieldType)); } else if (link.IsScoped(field)) { matches = GetModelsInScope(obj, allModels); } else { matches = GetChildren(obj); } } // Filter possible matches to those of the correct type. matches.RemoveAll(match => !fieldType.IsAssignableFrom(GetModel(match).GetType())); // If we should use name to match then filter matches to those with a matching name. if (link.UseNameToMatch(field)) { matches.RemoveAll(match => !StringUtilities.StringsAreEqual(GetName(match), field.Name)); } if (field.FieldType.IsArray) { Array array = Array.CreateInstance(fieldType, matches.Count); for (int i = 0; i < matches.Count; i++) { array.SetValue(GetModel(matches[i]), i); } field.SetValue(GetModel(obj), array); } else if (field.FieldType.Name.StartsWith("List") && field.FieldType.GenericTypeArguments.Length == 1) { var listType = typeof(List <>); var constructedListType = listType.MakeGenericType(fieldType); IList array = Activator.CreateInstance(constructedListType) as IList; for (int i = 0; i < matches.Count; i++) { array.Add(GetModel(matches[i])); } field.SetValue(GetModel(obj), array); } else if (matches.Count == 0) { if (!link.IsOptional) { throw new Exception("Cannot find a match for link " + field.Name + " in model " + GetFullName(obj)); } } else if (matches.Count >= 2 && !link.IsScoped(field)) { throw new Exception(string.Format(": Found {0} matches for link {1} in model {2} !", matches.Count, field.Name, GetFullName(obj))); } else { field.SetValue(GetModel(obj), GetModel(matches[0])); } } } }
/// <summary> /// Internal [link] resolution algorithm. /// </summary> /// <param name="obj"></param> /// <param name="scope">The scoping rules to use to resolve links.</param> /// <param name="throwOnFail">Should all links be considered optional?</param> private void ResolveInternal(object obj, ScopingRules scope, bool throwOnFail) { if (obj is Models.Management.RotationManager) { } foreach (IVariable field in GetAllDeclarations(GetModel(obj), GetModel(obj).GetType(), BindingFlags.Instance | BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public, allLinks: true)) { LinkAttribute link = field.GetAttribute(typeof(LinkAttribute)) as LinkAttribute; if (link != null) { Type fieldType = field.DataType; if (fieldType.IsArray) { fieldType = fieldType.GetElementType(); } else if (field.DataType.Name.StartsWith("List") && field.DataType.GenericTypeArguments.Length == 1) { fieldType = field.DataType.GenericTypeArguments[0]; } List <object> matches; matches = services.FindAll(s => fieldType.IsAssignableFrom(s.GetType())); if (matches.Count == 0 && obj is IModel) { Simulation parentSimulation = (obj as IModel).FindAncestor <Simulation>(); if (typeof(ILocator).IsAssignableFrom(fieldType) && parentSimulation != null) { matches.Add(new Locator(obj as IModel)); } else if (typeof(IEvent).IsAssignableFrom(fieldType) && parentSimulation != null) { matches.Add(new Events(obj as IModel)); } } if (matches.Count == 0) { if (link.Type == LinkType.Ancestor) { matches = new List <object>(); if (obj is IModel model) { IModel ancestor = GetParent(model, fieldType); if (ancestor == null) { throw new Exception($"Unable to resolve link {field.Name} in model {model.FullPath}: {model.Name} has no ancestors of type {fieldType.Name}"); } matches.Add(ancestor); } else { throw new Exception($"Unable to resolve ancestor link {field.Name} in object of type {obj.GetType()}: object is not a model"); } } else if (link.Type == LinkType.Path) { var locater = new Locater(); object match = locater.Get(link.Path, obj as Model); if (match != null) { matches.Add(match); } } else if (link.Type == LinkType.Scoped) { matches = scope.FindAll(obj as IModel).Cast <object>().ToList(); } else { matches = GetChildren(obj); } } matches.RemoveAll(match => !fieldType.IsAssignableFrom(GetModel(match).GetType())); if (link.ByName) { matches.RemoveAll(match => !StringUtilities.StringsAreEqual(GetName(match), field.Name)); } if (field.DataType.IsArray) { Array array = Array.CreateInstance(fieldType, matches.Count); for (int i = 0; i < matches.Count; i++) { array.SetValue(GetModel(matches[i]), i); } field.Value = array; } else if (field.DataType.Name.StartsWith("List") && field.DataType.GenericTypeArguments.Length == 1) { var listType = typeof(List <>); var constructedListType = listType.MakeGenericType(fieldType); IList array = Activator.CreateInstance(constructedListType) as IList; for (int i = 0; i < matches.Count; i++) { array.Add(GetModel(matches[i])); } field.Value = array; } else if (matches.Count == 0 && !throwOnFail) { if (!link.IsOptional) { throw new Exception("Cannot find a match for link " + field.Name + " in model " + GetFullName(obj)); } } else if (matches.Count >= 2 && link.Type != LinkType.Scoped) { throw new Exception(string.Format(": Found {0} matches for link {1} in model {2} !", matches.Count, field.Name, GetFullName(obj))); } else { field.Value = GetModel(matches[0]); } } } }