public static IReadOnlyCollection <Guid> CheckUniqueIds(IEnumerable <MemoryStream> validStreams) { HashSet <Guid> result = new HashSet <Guid>(); HashSet <Guid> existing = new HashSet <Guid>(); foreach (var stream in validStreams) { stream.Position = 0; var d = XDocument.Load(stream); var root = d.Element(Root); //TODO: Make sure the docs for this function (and the call chain) clarify that unsupported versions will be silently ignored. string encounteredVersion = root.Attribute("xmlversion")?.Value ?? ""; if (XmlVersionRead.Contains(encounteredVersion)) { var nodeElements = root.Elements("Node"); foreach (var guid in nodeElements.Select(n => Id <NodeTemp> .Parse(n.Attribute("Id").Value).Guid)) { if (!existing.Add(guid)) { result.Add(guid); } } } } return(result); }
public XmlGraphData <TUIRawData, TEditorData> Read(Stream stream) { object documentID = new object(); stream.Position = 0; var d = XDocument.Load(stream); var root = d.Element(Root); //TODO: Should possibly treat this as a missing file rather than crashing the editor string encounteredVersion = root.Attribute("xmlversion")?.Value ?? ""; if (!XmlVersionRead.Contains(encounteredVersion)) { throw new DeserializerVersionMismatchException(string.Join(", ", XmlVersionRead), encounteredVersion); } var nodeElements = root.Elements("Node"); var filteredNodes = m_filter != null?nodeElements.Where(n => m_filter(ReadType(n))) : nodeElements; IEnumerable <Either <GraphAndUI <TUIRawData>, LoadError> > editables = filteredNodes.Select(n => ReadEditable(n, m_datasource, documentID)).Evaluate(); var allnodes = new Dictionary <Id <NodeTemp>, GraphAndUI <TUIRawData> >(); var errors = new List <LoadError>(); foreach (var editable in editables) { editable.Do(e => { allnodes[e.GraphData.NodeId] = new GraphAndUI <TUIRawData>(e.GraphData, e.UIData); }, a => { errors.Add(a); }); } var links = ReadLinks(n => allnodes.ContainsKey(n), root); foreach (var link in links) { var id1 = link.Item1.Item2; var id2 = link.Item2.Item2; if (allnodes.ContainsKey(id1) && allnodes.ContainsKey(id2)) //If copy/pasting a piece of graph, linked nodes may not exist { IConversationNodeData node1 = allnodes[id1].GraphData; IConversationNodeData node2 = allnodes[id2].GraphData; var unknownNode1 = node1 as UnknownEditable; var unknownNode2 = node2 as UnknownEditable; if (unknownNode1 != null) { unknownNode1.AddConnector(link.Item1.Item1); } if (unknownNode2 != null) { unknownNode2.AddConnector(link.Item2.Item1); } Output connector1 = node1.Connectors.SingleOrDefault(c => c.Id == link.Item1.Item1); Output connector2 = node2.Connectors.SingleOrDefault(c => c.Id == link.Item2.Item1); if (unknownNode1 != null) { unknownNode1.AllowConnection(connector1, connector2); } if (unknownNode2 != null) { unknownNode2.AllowConnection(connector1, connector2); } if (connector1 == null || connector2 == null) { errors.Add(new LoadError("Connector does not exist")); } else { bool success = connector1.ConnectTo(connector2, false); if (!success) { success = connector1.ConnectTo(connector2, true); if (!success) { errors.Add(new LoadError("Tried to connect two connectors that could not be connected")); //TODO: Might be better to add the connection in violation of the rule to avoid modifying the file then have an error checker } } } } } foreach (var node in root.Elements("Node")) { Id <NodeTemp> nodeID = Id <NodeTemp> .Parse(node.Attribute("Id").Value); //ID<NodeTypeTemp> nodeType = ID<NodeTypeTemp>.Parse(node.Attribute("Guid").Value); int outputIndex = 0; //no idea where the output guids come from so just assume they're ordered //TODO: Remove support for legacy linking foreach (var output in node.Elements("Output")) { Id <TConnector> outputGuid = Id <TConnector> .Parse(output.Attribute("guid").Value); bool legacyNodeOutput = Id <TConnector> .Parse("1583e20c-c725-48c3-944d-1ba40c3ebdf4") == outputGuid; var outputConnectors = legacyNodeOutput ? allnodes[nodeID].GraphData.Connectors : allnodes[nodeID].GraphData.Connectors.ElementAt(outputIndex).Only(); var connectedNodes = output.Elements("Link").Select(l => Id <NodeTemp> .Parse(l.Attribute("To").Value)); foreach (var connectedID in connectedNodes) { var connectedNode = allnodes[connectedID].GraphData; var orderedConnectors = connectedNode.Connectors.OrderBy(o => o.Definition.Id != ConnectorDefinitionData.InputDefinitionId); //Put any inputs at the front of the list bool success = false; foreach (var connector in orderedConnectors) { if (success) { break; } foreach (var outputConnector in outputConnectors) { if (success) { break; } if (connector.ConnectTo(outputConnector, false)) { success = true; } else { connector.ConnectTo(outputConnector, true); } } } if (!success) { errors.Add(new LoadError("Could not link nodes")); } } outputIndex++; } } return(new XmlGraphData <TUIRawData, TEditorData>(allnodes.Values, m_editorDataDeserializer.Read(root), new ReadOnlyCollection <LoadError>(errors), documentID)); }