/// <summary> /// Reads the definition for a node, and adds it to the given agent group /// </summary> /// <param name="Element">Xml element to read the definition from</param> /// <param name="Group">Group for the node to be added to</param> /// <param name="ControllingTrigger">The controlling trigger for this node</param> void ReadNode(ScriptElement Element, AgentGroup Group, ManualTrigger ControllingTrigger) { string Name; if (EvaluateCondition(Element) && TryReadObjectName(Element, out Name)) { string[] RequiresNames = ReadListAttribute(Element, "Requires"); string[] ProducesNames = ReadListAttribute(Element, "Produces"); string[] AfterNames = ReadListAttribute(Element, "After"); // Resolve all the inputs we depend on HashSet <NodeOutput> Inputs = ResolveInputReferences(Element, RequiresNames); // Gather up all the input dependencies, and check they're all upstream of the current node HashSet <Node> InputDependencies = new HashSet <Node>(); foreach (Node InputDependency in Inputs.Select(x => x.ProducingNode).Distinct()) { if (InputDependency.ControllingTrigger != null && InputDependency.ControllingTrigger != ControllingTrigger && !InputDependency.ControllingTrigger.IsUpstreamFrom(ControllingTrigger)) { LogError(Element, "'{0}' is dependent on '{1}', which is behind a different controlling trigger ({2})", Name, InputDependency.Name, InputDependency.ControllingTrigger.QualifiedName); } else { InputDependencies.Add(InputDependency); } } // Recursively include all their dependencies too foreach (Node InputDependency in InputDependencies.ToArray()) { InputDependencies.UnionWith(InputDependency.InputDependencies); } // Add the name of the node itself to the list of outputs. List <string> OutputNames = new List <string>(); foreach (string ProducesName in ProducesNames) { if (ProducesName.StartsWith("#")) { OutputNames.Add(ProducesName.Substring(1)); } else { LogError(Element, "Output tag names must begin with a '#' character ('{0}')", ProducesName); } } OutputNames.Add(Name); // Gather up all the order dependencies HashSet <Node> OrderDependencies = new HashSet <Node>(InputDependencies); OrderDependencies.UnionWith(ResolveReferences(Element, AfterNames)); // Recursively include all their order dependencies too foreach (Node OrderDependency in OrderDependencies.ToArray()) { OrderDependencies.UnionWith(OrderDependency.OrderDependencies); } // Check that we're not dependent on anything completing that is declared after the initial declaration of this group. int GroupIdx = Graph.Groups.IndexOf(Group); for (int Idx = GroupIdx + 1; Idx < Graph.Groups.Count; Idx++) { foreach (Node Node in Graph.Groups[Idx].Nodes.Where(x => OrderDependencies.Contains(x))) { LogError(Element, "Node '{0}' has a dependency on '{1}', which was declared after the initial definition of '{2}'.", Name, Node.Name, Group.Name); } } // Construct and register the node if (CheckNameIsUnique(Element, Name)) { // Add it to the node lookup Node NewNode = new Node(Name, Inputs.ToArray(), OutputNames.ToArray(), InputDependencies.ToArray(), OrderDependencies.ToArray(), ControllingTrigger); Graph.NameToNode.Add(Name, NewNode); // Register each of the outputs as a reference to this node foreach (NodeOutput Output in NewNode.Outputs) { if (Output.Name == Name || CheckNameIsUnique(Element, Output.Name)) { Graph.NameToNodeOutput.Add(Output.Name, Output); } } // Add all the tasks EnterScope(); foreach (ScriptElement ChildElement in Element.ChildNodes.OfType <ScriptElement>()) { switch (ChildElement.Name) { case "Property": ReadProperty(ChildElement); break; case "Local": ReadLocalProperty(ChildElement); break; case "Warning": ReadDiagnostic(ChildElement, LogEventType.Warning, NewNode, Group, ControllingTrigger); break; case "Error": ReadDiagnostic(ChildElement, LogEventType.Error, NewNode, Group, ControllingTrigger); break; default: ReadTask(ChildElement, NewNode.Tasks); break; } } LeaveScope(); // Add it to the current agent group Group.Nodes.Add(NewNode); } } }
/// <summary> /// Reads a warning from the given element, evaluates the condition on it, and writes it to the log if the condition passes. /// </summary> /// <param name="Element">Xml element to read the definition from</param> /// <param name="EventType">The diagnostic event type</param> /// <param name="EnclosingObject">The enclosing object instance</param> void ReadDiagnostic(ScriptElement Element, LogEventType EventType, Node EnclosingNode, AgentGroup EnclosingGroup, ManualTrigger EnclosingTrigger) { if (EvaluateCondition(Element)) { string Message = ReadAttribute(Element, "Message"); GraphDiagnostic Diagnostic = new GraphDiagnostic(); Diagnostic.EventType = EventType; Diagnostic.Message = String.Format("{0}({1}): {2}", Element.File.FullName, Element.LineNumber, Message); Diagnostic.EnclosingNode = EnclosingNode; Diagnostic.EnclosingGroup = EnclosingGroup; Diagnostic.EnclosingTrigger = EnclosingTrigger; Graph.Diagnostics.Add(Diagnostic); } }
/// <summary> /// Reads the definition for an agent group. /// </summary> /// <param name="Element">Xml element to read the definition from</param> /// <param name="Trigger">The controlling trigger for nodes in this group</param> void ReadAgent(ScriptElement Element, ManualTrigger Trigger) { string Name; if (EvaluateCondition(Element) && TryReadObjectName(Element, out Name)) { // Read the valid agent types. This may be omitted if we're continuing an existing group. string[] Types = ReadListAttribute(Element, "Type"); // Create the group object, or continue an existing one AgentGroup Group; if (NameToGroup.TryGetValue(Name, out Group)) { if (Types.Length > 0 && Group.PossibleTypes.Length > 0) { string[] NewTypes = Group.PossibleTypes.Intersect(Types, StringComparer.InvariantCultureIgnoreCase).ToArray(); if (NewTypes.Length == 0) { LogError(Element, "No common agent types with previous agent definition"); } Group.PossibleTypes = NewTypes; } } else { if (Types.Length == 0) { LogError(Element, "Missing agent type for group '{0}'", Name); } Group = new AgentGroup(Name, Types); NameToGroup.Add(Name, Group); Graph.Groups.Add(Group); } // Process all the child elements. EnterScope(); foreach (ScriptElement ChildElement in Element.ChildNodes.OfType <ScriptElement>()) { switch (ChildElement.Name) { case "Property": ReadProperty(ChildElement); break; case "Local": ReadLocalProperty(ChildElement); break; case "Node": ReadNode(ChildElement, Group, Trigger); break; case "Aggregate": ReadAggregate(ChildElement); break; case "Warning": ReadDiagnostic(ChildElement, LogEventType.Warning, null, Group, Trigger); break; case "Error": ReadDiagnostic(ChildElement, LogEventType.Error, null, Group, Trigger); break; default: LogError(ChildElement, "Unexpected element type '{0}'", ChildElement.Name); break; } } LeaveScope(); } }