/// <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])); } } } }