/// <summary> /// Method to display information about a particular node to the user. /// </summary> /// <param name="current">The Node whose information we want to display.</param> /// <returns>A string with information regarding the input node.</returns> private string GetNodeDetail(PartNode current) { string startMessage; if (current.IsRejectionExpected) { startMessage = Strings.RejectionExpectedLabel; } else { startMessage = string.Empty; } if (this.Options.Verbose) { StringWriter writer = new StringWriter(new StringBuilder()); foreach (string errorMessage in current.VerboseMessages) { string message = startMessage + errorMessage; writer.WriteLine(message); writer.WriteLine(); } return(writer.ToString()); } else { string message = startMessage + this.GetName(current.Part, Strings.VerbosePartLabel); return(message); } }
/// <summary> /// Method to indicate all the rejection issues present in a given level. /// </summary> /// <param name="currentLevel">An integer representing the level we are intrested in.</param> private void ListErrorsinLevel(int currentLevel) { string errorMessage = string.Format( CultureInfo.CurrentCulture, Strings.ErrorsLevelMessage, currentLevel); this.Options.Writer.WriteLine(errorMessage); List <string> currentErrors = new List <string>(); foreach (var pair in this.RejectionGraph) { PartNode currentNode = pair.Value; if (currentNode.Level.Equals(currentLevel)) { currentErrors.Add(this.GetNodeDetail(currentNode)); } } this.WriteLines(currentErrors); if (!this.Options.Verbose) { this.Options.Writer.WriteLine(); } }
/// <summary> /// Method to add a node that the current node caused a rejection error in. /// </summary> /// <param name="node">The node that the current node caused the error in.</param> /// <param name="description">Label to use when visualizing the edge.</param> internal void AddParent(PartNode node, string description = "") => this.RejectsCaused.Add(new PartEdge(node, description));
/// <summary> /// Method to add a node that caused a rejection error in the current node. /// </summary> /// <param name="node">The node that is the cause of the error.</param> /// <param name="description">Label to use when visualizing the edge.</param> internal void AddChild(PartNode node, string description = "") => this.ImportRejects.Add(new PartEdge(node, description));
/// <summary> /// Method to initialize the part nodes and thier "pointers" based on the error /// stack from the config. /// </summary> private void GenerateNodeGraph() { // Get the error stack from the composition configuration var expectedRejectionsChecker = new ExpectedRejections(this.Options); CompositionConfiguration config = this.Creator.Config !; var errors = config.CompositionErrors; int levelNumber = 1; while (errors.Count() > 0) { // Process all the parts present in the current level of the stack var currentLevel = errors.Peek(); foreach (var element in currentLevel) { var part = element.Parts.First(); // Create a PartNode object from the definition of the current Part ComposablePartDefinition definition = part.Definition; string currentName = definition.Type.FullName !; if (currentName == null) { continue; } if (this.RejectionGraph.ContainsKey(currentName)) { this.RejectionGraph[currentName].AddErrorMessage(element.Message); continue; } PartNode currentNode = new PartNode(definition, element.Message, levelNumber); currentNode.IsRejectionExpected = expectedRejectionsChecker.IsRejectionExpected(currentName); this.RejectionGraph.Add(currentName, currentNode); } // Get the next level of the stack errors = errors.Pop(); levelNumber += 1; } this.MaxLevels = levelNumber - 1; foreach (var nodePair in this.RejectionGraph) { var node = nodePair.Value; var currentNodeName = node.Name; var nodeDefinition = node.Part; // Get the imports for the current part to update the pointers associated with the current node foreach (var import in nodeDefinition.Imports) { string importName = import.ImportingSiteType.FullName !; if (importName == null || !this.RejectionGraph.ContainsKey(importName)) { continue; } string importLabel = importName; if (import.ImportingMember != null) { importLabel = import.ImportingMember.Name; } PartNode childNode = this.RejectionGraph[importName]; childNode.AddParent(node, importLabel); node.AddChild(childNode, importLabel); } } }
/// <summary> /// Method to the get the information about the rejection information that caused a /// particular import failure, rather than for the entire system. /// If graph was specified in the input arguments then a DGML graph tracing the rejection /// chain assocaited with the current path alone is saved to a file called [partName].dgml. /// </summary> /// <param name = "partName"> The name of the part which we want to analyze.</param> /// <remarks> /// Once again, the root causes can easily be accessed by looking at the rejection /// issues at the highest levels of the output. /// </remarks> private void ListReject(string partName) { string individualRejection = string.Format( CultureInfo.CurrentCulture, Strings.IndividualRejectionMessage, partName); this.Options.Writer.WriteLine(individualRejection); // Deal with the case that there are no rejection issues with the given part if (!this.RejectionGraph.ContainsKey(partName)) { string noRejection = string.Format( CultureInfo.CurrentCulture, Strings.NoRejectionMessage, partName); this.Options.ErrorWriter.WriteLine(noRejection); return; } // Store just the nodes that are involved in the current rejection chain to use when generating the graph Dictionary <string, PartNode> relevantNodes = new Dictionary <string, PartNode>(); bool saveGraph = this.Options.GraphPath != null && this.Options.GraphPath.Length > 0; // Perform Breadth First Search (BFS) with the node associated with partName as the root. // When performing BFS, only the child nodes are considered since we want to the root to be // the end point of the rejection chain(s). // BFS was chosen over DFS because of the fact that we process level by level when performing // the travesal and thus easier to communicate the causes and pathway to the end user Queue <PartNode> currentLevelNodes = new Queue <PartNode>(); currentLevelNodes.Enqueue(this.RejectionGraph[partName]); while (currentLevelNodes.Count() > 0) { int currentLevel = currentLevelNodes.Peek().Level; string errorLevel = string.Format( CultureInfo.CurrentCulture, Strings.ErrorsLevelMessage, currentLevel); this.Options.Writer.WriteLine(errorLevel); // Iterate through all the nodes in the current level int numNodes = currentLevelNodes.Count(); List <string> errorMessages = new List <string>(numNodes); for (int index = 0; index < numNodes; index++) { // Process the current node by displaying its import issue and adding it to the graph PartNode current = currentLevelNodes.Dequeue(); if (saveGraph) { relevantNodes.Add(current.Name, current); } errorMessages.Add(this.GetNodeDetail(current)); // Add the "children" of the current node to the queue for future processing if (current.ImportRejects.Count() > 0) { foreach (var childEdge in current.ImportRejects) { currentLevelNodes.Enqueue(childEdge.Target); } } } this.WriteLines(errorMessages); if (!this.Options.Verbose) { this.Options.Writer.WriteLine(); } } // Save the output graph if the user request it if (saveGraph) { GraphCreator nodeGraph = new GraphCreator(relevantNodes); // Replacing '.' with '_' in the fileName to ensure that the '.' is associated with the file extension string fileName = partName.Replace(".", "_") + ".dgml"; this.SaveGraph(fileName, nodeGraph); } }