protected void MatchWithContextsReferencingSubContext(List <ContextNode> matches) { // For each match, see if there are other super-contexts that reference this same matching context. // If so, these should be added to the list of matches. // TODO: Would this be easier if we change ContextNode.Parent from a singleton to a List<ContextNode> ? // Clone, otherwise we may end up modifying the list of known matches. foreach (ContextNode match in matches.ToList()) { List <Guid> matchParentPath = match.GetParentInstancePath(); // We only want root contexts. foreach (var nodes in flatView.Values.Where(n => n.Last().Parent.InstanceId == Guid.Empty)) { ContextNode lastNode = nodes.Last(); foreach (var childNodeIdPath in lastNode.ChildInstancePaths()) { // We attempt to find a node where any sub-context ID equals the match last ID, as this gives us at least // one context node in which we know that there is another reference. // This, incidentally, is the common context type that we're searching on. if (childNodeIdPath.Any(id => id == match.InstanceId)) { // The root instance ID for this match should be different than any existing match. if (!matches.Any(m => m.GetParentInstancePath().First() == nodes.First().InstanceId)) { // Add this other context, that is referencing the context we matched on. matches.Add(lastNode); } } } } } }
public bool TryGetContextNode(Field field, Guid rootId, int recordNumber, out ContextNode node, out List <Guid> instanceIdPath) { instanceIdPath = new List <Guid>(); instanceIdPath.Add(rootId); ContextNode dictNode = tree.Children.Single(c => c.InstanceId == rootId); // Traverse the dictionary up to the node containing the ContextValue. // We traverse by type, building the instance ID list as we go. for (int i = 1; i < (field.ContextPath.Count - 1) && dictNode != null; i++) { dictNode = dictNode.Children.SingleOrDefault(c => c.Type == field.ContextPath[i].Type); if (dictNode != null) { instanceIdPath.Add(dictNode.InstanceId); } } if (dictNode != null) { // Acquire the ContextValue based on field type and record number. dictNode = dictNode.Children.SingleOrDefault(c => c.Type == field.ContextPath[field.ContextPath.Count - 1].Type && c.ContextValue.RecordNumber == recordNumber); if (dictNode != null) { instanceIdPath.Add(dictNode.InstanceId); } } node = dictNode; return(node != null); }
public Type GetRootType(Guid rootId) { ContextNode dictNode = tree.Children.Single(c => c.InstanceId == rootId); Type rootType = dictNode.Type; return(rootType); }
public IReadOnlyList <ContextValue> GetContextValues(ContextNode node) { List <ContextValue> contextValues = new List <ContextValue>(); GetContextValues(contextValues, node); return(contextValues.AsReadOnly()); }
public string Render(ContextNode contextNode, ContextValueDictionary cvd, int recNum, IReadOnlyList <ContextValue> contextValues) { StringBuilder sb = new StringBuilder(); foreach (var component in components) { sb.Append(component.Render(contextNode, cvd, recNum, contextValues)); } return(sb.ToString()); }
protected void GetContextValues(List <ContextValue> contextValues, ContextNode node) { foreach (var child in node.Children) { GetContextValues(contextValues, child); if (child.ContextValue != null) { contextValues.Add(child.ContextValue); } } }
public override string Render(ContextNode contextNode, ContextValueDictionary cvd, int recNum, IReadOnlyList <ContextValue> contextValues) { var contextValue = contextValues.SingleOrDefault(cv => cv.Type == ValueEntity && cv.RecordNumber == recNum); string ret = String.Empty; if (contextValue != null) { ret = contextValue.Value; } return(ret); }
/// <summary> /// The parent instance chain from this child, including this child's ID. /// </summary> public List <Guid> GetParentInstancePath() { List <Guid> path = new List <Guid>(); path.Add(InstanceId); ContextNode parentNode = Parent; while (parentNode != null) { path.Insert(0, parentNode.InstanceId); parentNode = parentNode.Parent; } return(path.Skip(1).ToList()); // Exclude the Guid.Empty root. }
protected ContextNode CreateNode(int i, Guid id, ContextValue cv, ContextNode node, Type type) { if (i == cv.TypePath.Count - 1) { // At this point, node.Children[].Type && node.Children[].ContextValue.RecordNumber must be unique! Assert.That <ContextValueDictionaryException>(!node.Children.Any(c => c.Type == type && c.ContextValue.RecordNumber == cv.RecordNumber), "ContextValue type and record number must be unique to parent context."); } ContextNode childNode = new ContextNode(id, type); node.AddChild(childNode); // Since we're creating a node, add it to the flat tree view. if (!flatView.TryGetValue(type, out List <ContextNode> nodes)) { flatView[type] = new List <ContextNode>(); } flatView[type].Add(childNode); return(childNode); }
public void Load() { // https://stackoverflow.com/questions/7397207/json-net-error-self-referencing-loop-detected-for-type var settings = new JsonSerializerSettings(); settings.ReferenceLoopHandling = ReferenceLoopHandling.Serialize; settings.PreserveReferencesHandling = PreserveReferencesHandling.Objects; settings.TypeNameHandling = TypeNameHandling.Auto; settings.ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor; settings.ObjectCreationHandling = ObjectCreationHandling.Auto; if (File.Exists("tree.cvd")) { string jsonTree = File.ReadAllText("tree.cvd"); tree = JsonConvert.DeserializeObject <ContextNode>(jsonTree, settings); } if (File.Exists("flatView.cvd")) { string jsonFlatView = File.ReadAllText("flatView.cvd"); flatView = JsonConvert.DeserializeObject <Dictionary <Type, List <ContextNode> > >(jsonFlatView, settings); } }
/// <summary> /// Give a child node, returns the full path, starting from the topmost node, of the node hierarchy. /// </summary> public ContextNodePath GetPath(ContextNode node) { // A LINQ way of traversing the tree to its root. Seems more complicated than the approach here. // https://stackoverflow.com/questions/34741456/linq-traverse-upwards-and-retrieve-parent-child-relationship // Also an interesting read: // https://www.codeproject.com/Articles/62397/LINQ-to-Tree-A-Generic-Technique-for-Querying-Tree Type rootType = node.Type; Guid rootGuid = node.InstanceId; List <NodeTypeInstance> path = new List <NodeTypeInstance>() { new NodeTypeInstance() { Type = node.Type, InstanceId = node.InstanceId } }; var parent = node.Parent; while (parent != null && parent.InstanceId != Guid.Empty) { rootType = parent.Type; rootGuid = parent.InstanceId; path.Insert(0, new NodeTypeInstance() { Type = parent.Type, InstanceId = parent.InstanceId }); parent = parent.Parent; } var match = new ContextNodePath() { Type = rootType, Name = rootType.Name, Path = path }; return(match); }
public void AddOrUpdate(ContextValue cv) { // We have to process this synchronously! // If async, we might get simultaneous requests (particularly from the browser's async PUT calls) to add a value. // While we're constructing the dictionary entry for one context path, another request might come in before we've // created all the nodes for the first call. lock (this) { // Walk the instance/path, creating new nodes in the context tree as required. Assert.That(cv.TypePath.Count == cv.InstancePath.Count, "type path and instance path should have the same number of entries."); ContextNode node = tree; for (int i = 0; i < cv.TypePath.Count; i++) { // Walk the tree. var(id, type) = (cv.InstancePath[i], cv.TypePath[i]); if (node.Children.TryGetSingle(c => c.InstanceId == id, out ContextNode childNode)) { node = childNode; } else { // Are we referencing an existing sub-context? if (flatView.TryGetValue(type, out List <ContextNode> nodes)) { // The instance path of the node must match all the remaining instance paths in the context // we're adding/updating! bool foundExistingSubContext = false; foreach (var fvnode in nodes) { foreach (var fvnodepath in fvnode.ChildInstancePaths()) { if (cv.InstancePath.Skip(i).SequenceEqual(fvnodepath)) { // This node get's a child referencing the existing sub-context node. node.AddChild(fvnode); node = fvnode; foundExistingSubContext = true; break; } } if (foundExistingSubContext) { break; } } if (!foundExistingSubContext) { node = CreateNode(i, id, cv, node, type); } } else { node = CreateNode(i, id, cv, node, type); } } } // The last entry in the tree gets the actual context value. We've either added this node to the tree // or updating an existing node. node.ContextValue = cv; } }
public void AddChild(ContextNode node) { children.Add(node); node.Parent = this; }
public override string Render(ContextNode contextNode, ContextValueDictionary cvd, int recNum, IReadOnlyList <ContextValue> contextValues) { return(Text); }
public abstract string Render(ContextNode contextNode, ContextValueDictionary cvd, int recNum, IReadOnlyList <ContextValue> contextValues);