/// <summary>Connect all links and events in simulation</summary> public void ConnectLinksAndEvents() { scope = new ScopingRules(); events = new Events(this); events.ConnectEvents(); links = new Core.Links(); links.Resolve(this); }
/// <summary> /// Returns all models which are in scope. /// </summary> public IEnumerable <IModel> FindAllInScope() { Simulation sim = FindAncestor <Simulation>(); ScopingRules scope = sim?.Scope ?? new ScopingRules(); foreach (IModel result in scope.FindAll(this)) { yield return(result); } }
/// <summary> /// Locates and returns all models in scope. /// </summary> /// <param name="model">The reference model</param> /// <returns>The found models or an empty array if not found.</returns> public static List <IModel> FindAll(IModel model) { var simulation = Apsim.Parent(model, typeof(Simulation)) as Simulation; if (simulation == null || simulation.Scope == null) { ScopingRules scope = new ScopingRules(); return(scope.FindAll(model).ToList()); } return(simulation.Scope.FindAll(model).ToList()); }
/// <summary> /// Return a list of subscribers that are in scope. /// </summary> /// <param name="relativeTo">Model to base scoping rules on.</param> /// <param name="cache">The model/scriber cache</param> /// <param name="scope">An instance of scoping rules</param> /// <param name="subscribers">A collection of all subscribers</param> private static List <Subscriber> FilterSubscribersInScope(Publisher relativeTo, Dictionary <IModel, List <Subscriber> > cache, ScopingRules scope, List <Events.Subscriber> subscribers) { // Try cache List <Subscriber> subscribersInScope; if (cache.TryGetValue(relativeTo.Model as IModel, out subscribersInScope)) { return(subscribersInScope); } List <IModel> modelsInScope = new List <IModel>(scope.FindAll(relativeTo.Model as IModel)); subscribersInScope = new List <Subscriber>(); subscribersInScope = subscribers.FindAll(subscriber => modelsInScope.Contains(subscriber.Model as IModel)); cache.Add(relativeTo.Model as IModel, subscribersInScope); return(subscribersInScope); }
/// <summary> /// /// </summary> /// <param name="rootNode"></param> /// <param name="recurse">Recurse through all child models?</param> /// <param name="allLinks">Unresolve all links or just the non child links?</param> public void Resolve(IModel rootNode, bool allLinks, bool recurse = true) { var scope = new ScopingRules(); if (recurse) { List <IModel> allModels = new List <IModel>() { rootNode }; allModels.AddRange(Apsim.ChildrenRecursively(rootNode)); foreach (IModel modelNode in allModels) { if (modelNode.Enabled) { ResolveInternal(modelNode, scope); } } } else { ResolveInternal(rootNode, scope); } }
/// <summary> /// /// </summary> /// <param name="rootNode"></param> /// <param name="recurse">Recurse through all child models?</param> /// <param name="allLinks">Unresolve all links or just the non child links?</param> /// <param name="throwOnFail">Should all links be considered optional?</param> public void Resolve(IModel rootNode, bool allLinks, bool recurse = true, bool throwOnFail = false) { var scope = new ScopingRules(); if (recurse) { List <IModel> allModels = new List <IModel>() { rootNode }; allModels.AddRange(rootNode.FindAllDescendants()); foreach (IModel modelNode in allModels) { if (modelNode.Enabled) { ResolveInternal(modelNode, scope, throwOnFail); } } } else { ResolveInternal(rootNode, scope, throwOnFail); } }
/// <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> /// Clears the existing Scoping Rules /// </summary> public void ClearScopingRules() { scope = new ScopingRules(); }
/// <summary>Constructor</summary> public Simulation() { locater = new Locater(); scope = new ScopingRules(); }
/// <summary>Find all event subscribers in the specified models.</summary> /// <param name="name">The name of the event to look for</param> /// <param name="relativeTo">The model to use in scoping lookup</param> /// <param name="scope">Scoping rules</param> /// <returns>The list of event subscribers</returns> internal static List <Subscriber> FindAll(string name, IModel relativeTo, ScopingRules scope) { List <Subscriber> subscribers = new List <Subscriber>(); foreach (IModel modelNode in scope.FindAll(relativeTo)) { foreach (MethodInfo method in modelNode.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)) { EventSubscribeAttribute subscriberAttribute = (EventSubscribeAttribute)ReflectionUtilities.GetAttribute(method, typeof(EventSubscribeAttribute), false); if (subscriberAttribute != null && subscriberAttribute.ToString() == name) { subscribers.Add(new Subscriber(subscriberAttribute.ToString(), modelNode, method)); } } } return(subscribers); }
internal static Dictionary <string, List <Subscriber> > GetAll(string name, IModel relativeTo, ScopingRules scope) { IModel[] allModels = scope.FindAll(relativeTo); Dictionary <string, List <Subscriber> > subscribers = new Dictionary <string, List <Subscriber> >(); foreach (IModel modelNode in allModels) { foreach (MethodInfo method in modelNode.GetType().GetMethods(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)) { EventSubscribeAttribute attribute = (EventSubscribeAttribute)ReflectionUtilities.GetAttribute(method, typeof(EventSubscribeAttribute), false); if (attribute != null) { string eventName = attribute.ToString(); if (!eventName.Equals(name, StringComparison.InvariantCultureIgnoreCase)) { continue; } Subscriber subscriber = new Subscriber(eventName, modelNode, method); if (subscribers[eventName] == null) { subscribers[eventName] = new List <Subscriber>(); } subscribers[eventName].Add(subscriber); } } } return(subscribers); }
/// <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]); } } } }