/// <summary> /// Set the <see cref="TargetNode"/> and <see cref="TargetGuid"/> of the targeted object by retrieving it from or creating it to the given <see cref="NodeContainer"/>. /// </summary> /// <param name="nodeContainer">The <see cref="NodeContainer"/> used to retrieve or create the target node.</param> /// <param name="nodeFactory">The factory to use to create nodes.</param> internal IGraphNode SetTarget(NodeContainer nodeContainer, NodeFactoryDelegate nodeFactory) { if (nodeContainer == null) { throw new ArgumentNullException(nameof(nodeContainer)); } IGraphNode targetNode = nodeContainer.GetOrCreateNode(ObjectValue, nodeFactory); if (targetNode != null) { if (targetNode.Content.Value != null && !Type.IsInstanceOfType(targetNode.Content.Value)) { throw new InvalidOperationException(@"The type of the retrieved node content does not match the type of this reference"); } if (TargetNode != null || TargetGuid != Guid.Empty) { throw new InvalidOperationException("TargetNode has already been set."); } if (targetNode.Content.Value != null && !Type.IsInstanceOfType(targetNode.Content.Value)) { throw new InvalidOperationException("TargetNode type does not match the reference type."); } TargetNode = targetNode; TargetGuid = targetNode.Guid; } return(targetNode); }
/// <summary> /// Creates a graph node. /// </summary> /// <param name="rootObject">The root object.</param> /// <param name="nodeFactory">The factory to use to create nodes.</param> /// <returns>A new graph node representing the given root object.</returns> private IGraphNode CreateNode(object rootObject, NodeFactoryDelegate nodeFactory) { if (rootObject == null) { throw new ArgumentNullException(nameof(rootObject)); } Guid guid = Guid.NewGuid(); // Retrieve results if (guidContainer != null && !rootObject.GetType().IsValueType) { guid = guidContainer.GetOrCreateGuid(rootObject); } var result = (GraphNode)NodeBuilder.Build(rootObject, guid, nodeFactory); if (result != null) { // Register the factory used to create this node. factoriesByNode.Add(result, nodeFactory); // Register reference objects nodesByGuid.Add(result.Guid, result); // Create or update nodes of referenced objects UpdateReferences(result); } return(result); }
/// <inheritdoc/> public void OverrideNodeFactory(NodeFactoryDelegate nodeFactory) { lock (lockObject) { defaultNodeFactory = nodeFactory; } }
/// <summary> /// Set the <see cref="TargetNode"/> and <see cref="TargetGuid"/> of the targeted object by retrieving it from or creating it to the given <see cref="NodeContainer"/>. /// </summary> /// <param name="nodeContainer">The <see cref="NodeContainer"/> used to retrieve or create the target node.</param> /// <param name="nodeFactory">The factory to use to create nodes.</param> internal IGraphNode SetTarget(NodeContainer nodeContainer, NodeFactoryDelegate nodeFactory) { if (nodeContainer == null) { throw new ArgumentNullException(nameof(nodeContainer)); } IGraphNode targetNode = nodeContainer.GetOrCreateNodeInternal(ObjectValue, nodeFactory); if (targetNode != null) { if (targetNode.Content.Value != null && !Type.IsInstanceOfType(targetNode.Content.Value)) { throw new InvalidOperationException(@"The type of the retrieved node content does not match the type of this reference"); } // TODO: Disabled this exception which is triggered when a circular reference is made. This will be properly fixed when we'll get rid of the ShouldProcessReference mechanism. //if (TargetNode != null || TargetGuid != Guid.Empty) // throw new InvalidOperationException("TargetNode has already been set."); if (targetNode.Content.Value != null && !Type.IsInstanceOfType(targetNode.Content.Value)) { throw new InvalidOperationException("TargetNode type does not match the reference type."); } TargetNode = targetNode; TargetGuid = targetNode.Guid; } return(targetNode); }
/// <summary> /// Registers the default factory to use to create <see cref="IGraphNode"/> instances. /// </summary> /// <param name="nodeFactory">The factory to register.</param> public void RegisterDefaultFactory(NodeFactoryDelegate nodeFactory) { lock (lockObject) { nodeFactories[new NodeFactoryId(Guid.Empty)] = nodeFactory; defaultNodeFactory = nodeFactory; } }
public NodeFactoryId RegisterFactory(NodeFactoryDelegate nodeFactory) { lock (lockObject) { var id = new NodeFactoryId(Guid.NewGuid()); nodeFactories.Add(id, nodeFactory); return id; } }
public NodeFactoryId RegisterFactory(NodeFactoryDelegate nodeFactory) { lock (lockObject) { var id = new NodeFactoryId(Guid.NewGuid()); nodeFactories.Add(id, nodeFactory); return(id); } }
/// <inheritdoc/> public IGraphNode Build(object obj, Guid guid, NodeFactoryDelegate nodeFactory) { if (obj == null) throw new ArgumentNullException(nameof(obj)); if (nodeFactory == null) throw new ArgumentNullException(nameof(nodeFactory)); Reset(); rootGuid = guid; var typeDescriptor = TypeDescriptorFactory.Find(obj.GetType()); currentNodeFactory = nodeFactory; VisitObject(obj, typeDescriptor as ObjectDescriptor, true); currentNodeFactory = null; return rootNode; }
internal void Refresh(IGraphNode ownerNode, NodeContainer nodeContainer, NodeFactoryDelegate nodeFactory, Index index) { var objectValue = ownerNode.Content.Retrieve(index); if (TargetNode?.Content.Value != objectValue) { // This call will recursively update the references. var target = SetTarget(objectValue, nodeContainer, nodeFactory); if (target != null) { var ownerContent = (ContentBase)ownerNode.Content; var boxedContent = target.Content as BoxedContent; boxedContent?.SetOwnerContent(ownerContent, index); } } // This reference is not orphan anymore. orphanObject = null; }
/// <summary> /// Set the <see cref="TargetNode"/> and <see cref="TargetGuid"/> of the targeted object by retrieving it from or creating it to the given <see cref="NodeContainer"/>. /// </summary> /// <param name="nodeContainer">The <see cref="NodeContainer"/> used to retrieve or create the target node.</param> /// <param name="nodeFactory">The factory to use to create nodes.</param> internal IGraphNode SetTarget(NodeContainer nodeContainer, NodeFactoryDelegate nodeFactory) { if (nodeContainer == null) throw new ArgumentNullException(nameof(nodeContainer)); IGraphNode targetNode = nodeContainer.GetOrCreateNode(ObjectValue, nodeFactory); if (targetNode != null) { if (targetNode.Content.Value != null && !Type.IsInstanceOfType(targetNode.Content.Value)) throw new InvalidOperationException(@"The type of the retrieved node content does not match the type of this reference"); if (TargetNode != null || TargetGuid != Guid.Empty) throw new InvalidOperationException("TargetNode has already been set."); if (targetNode.Content.Value != null && !Type.IsInstanceOfType(targetNode.Content.Value)) throw new InvalidOperationException("TargetNode type does not match the reference type."); TargetNode = targetNode; TargetGuid = targetNode.Guid; } return targetNode; }
public void Refresh(IGraphNode ownerNode, NodeContainer nodeContainer, NodeFactoryDelegate nodeFactory) { var newObjectValue = ownerNode.Content.Value; if (!(newObjectValue is IEnumerable)) { throw new ArgumentException(@"The object is not an IEnumerable", nameof(newObjectValue)); } ObjectValue = newObjectValue; // First, let's build a new list of uninitialized references var newReferences = new List <ObjectReference>(IsDictionary ? ((IEnumerable)ObjectValue).Cast <object>().Select(x => (ObjectReference)Reference.CreateReference(GetValue(x), elementType, GetKey(x))) : ((IEnumerable)ObjectValue).Cast <object>().Select((x, i) => (ObjectReference)Reference.CreateReference(x, elementType, new Index(i)))); // The reference need to be updated if it has never been initialized, if the number of items is different, or if any index or any value is different. var needUpdate = references == null || newReferences.Count != references.Count || newReferences.Zip(references).Any(x => !x.Item1.Index.Equals(x.Item2.Index) || !Equals(x.Item1.ObjectValue, x.Item2.ObjectValue)); if (needUpdate) { // We create a dictionary that maps values of the old list of references to their corresponding target node. var dictionary = references?.Where(x => x.ObjectValue != null && !(x.TargetNode?.Content is BoxedContent)).ToDictionary(x => x.ObjectValue) ?? new Dictionary <object, ObjectReference>(); foreach (var newReference in newReferences) { if (newReference.ObjectValue != null) { ObjectReference oldReference; if (dictionary.TryGetValue(newReference.ObjectValue, out oldReference)) { // If this value was already present in the old list of reference, just use the same target node in the new list. newReference.SetTarget(oldReference.TargetNode); } else { // Otherwise, do a full update that will properly initialize the new reference. newReference.Refresh(ownerNode, nodeContainer, nodeFactory, newReference.Index); } } } references = newReferences; indices = newReferences.Select(x => x.Index).ToList(); } }
/// <inheritdoc/> public IGraphNode Build(object obj, Guid guid, NodeFactoryDelegate nodeFactory) { if (obj == null) { throw new ArgumentNullException(nameof(obj)); } if (nodeFactory == null) { throw new ArgumentNullException(nameof(nodeFactory)); } Reset(); rootGuid = guid; var typeDescriptor = TypeDescriptorFactory.Find(obj.GetType()); currentNodeFactory = nodeFactory; VisitObject(obj, typeDescriptor as ObjectDescriptor, true); currentNodeFactory = null; return(rootNode); }
/// <summary> /// Gets the node associated to a data object, if it exists, otherwise creates a new node for the object and its member recursively. /// </summary> /// <param name="rootObject">The data object.</param> /// <param name="nodeFactory">The factory to use to create nodes.</param> /// <returns>The <see cref="IGraphNode"/> associated to the given object.</returns> internal IGraphNode GetOrCreateNode(object rootObject, NodeFactoryDelegate nodeFactory) { if (nodeFactory == null) { throw new ArgumentNullException(nameof(nodeFactory)); } if (rootObject == null) { return(null); } lock (lockObject) { IGraphNode result = null; if (guidContainer != null && !rootObject.GetType().IsValueType) { result = GetNode(rootObject); } return(result ?? CreateNode(rootObject, nodeFactory)); } }
/// <summary> /// Gets the node associated to a data object, if it exists, otherwise creates a new node for the object and its member recursively. /// </summary> /// <param name="rootObject">The data object.</param> /// <param name="nodeFactory">The factory to use to create nodes.</param> /// <returns>The <see cref="IGraphNode"/> associated to the given object.</returns> internal IGraphNode GetOrCreateNodeInternal(object rootObject, NodeFactoryDelegate nodeFactory) { if (nodeFactory == null) { throw new ArgumentNullException(nameof(nodeFactory)); } if (rootObject == null) { return(null); } lock (lockObject) { IGraphNode result; if (!rootObject.GetType().IsValueType) { result = GetNodeInternal(rootObject); if (result != null) { return(result); } } var guid = !rootObject.GetType().IsValueType ? guidContainer.GetOrCreateGuid(rootObject) : Guid.NewGuid(); result = NodeBuilder.Build(rootObject, guid, nodeFactory); if (result != null) { // Register reference objects nodesByGuid.Add(result.Guid, result); // Create or update nodes of referenced objects UpdateReferencesInternal(result); } return(result); } }
public FunctionalNodeProvider(string path, NodeFactoryDelegate factoryMethod) { Path = path; ParentPath = N2.Web.Url.RemoveLastSegment(Path); FactoryMethod = factoryMethod; }
private void UpdateOrCreateReferenceTarget(IReference reference, IGraphNode node, NodeFactoryDelegate nodeFactory, Stack <object> indices = null) { if (reference == null) { throw new ArgumentNullException(nameof(reference)); } if (node == null) { throw new ArgumentNullException(nameof(node)); } var content = (ContentBase)node.Content; var referenceEnumerable = reference as ReferenceEnumerable; if (referenceEnumerable != null) { if (indices == null) { indices = new Stack <object>(); } foreach (var itemReference in referenceEnumerable) { indices.Push(itemReference.Index); UpdateOrCreateReferenceTarget(itemReference, node, nodeFactory, indices); indices.Pop(); } } else { if (content.ShouldProcessReference) { var singleReference = ((ObjectReference)reference); if (singleReference.TargetNode != null && singleReference.TargetNode.Content.Value != reference.ObjectValue) { singleReference.Clear(); } if (singleReference.TargetNode == null && reference.ObjectValue != null) { // This call will recursively update the references. var target = singleReference.SetTarget(this, nodeFactory); if (target != null) { var structContent = target.Content as BoxedContent; if (structContent != null) { structContent.BoxedStructureOwner = content; structContent.BoxedStructureOwnerIndices = indices?.Reverse().ToArray(); } } else { content.ShouldProcessReference = false; } } } } }
public void Refresh(IGraphNode ownerNode, NodeContainer nodeContainer, NodeFactoryDelegate nodeFactory) { var newObjectValue = ownerNode.Content.Value; if (!(newObjectValue is IEnumerable)) { throw new ArgumentException(@"The object is not an IEnumerable", nameof(newObjectValue)); } ObjectValue = newObjectValue; var newReferences = new HybridDictionary <Index, ObjectReference>(); if (IsDictionary) { foreach (var item in (IEnumerable)ObjectValue) { var key = GetKey(item); var value = (ObjectReference)Reference.CreateReference(GetValue(item), elementType, key); newReferences.Add(key, value); } } else { var i = 0; foreach (var item in (IEnumerable)ObjectValue) { var key = new Index(i); var value = (ObjectReference)Reference.CreateReference(item, elementType, key); newReferences.Add(key, value); ++i; } } // The reference need to be updated if it has never been initialized, if the number of items is different, or if any index or any value is different. var needUpdate = items == null || newReferences.Count != items.Count || !AreItemsEqual(items, newReferences); if (needUpdate) { // We create a mapping values of the old list of references to their corresponding target node. We use a list because we can have multiple times the same target in items. var oldReferenceMapping = new List <KeyValuePair <object, ObjectReference> >(); if (items != null) { oldReferenceMapping.AddRange(items.Values.Where(x => x.ObjectValue != null && !(x.TargetNode?.Content is BoxedContent)).Select(x => new KeyValuePair <object, ObjectReference>(x.ObjectValue, x))); } foreach (var newReference in newReferences) { if (newReference.Value.ObjectValue != null) { var found = false; var i = 0; foreach (var item in oldReferenceMapping) { if (Equals(newReference.Value.ObjectValue, item.Key)) { // If this value was already present in the old list of reference, just use the same target node in the new list. newReference.Value.SetTarget(item.Value.TargetNode); // Remove consumed existing reference so if there is a second entry with the same "key", it will be the other reference that will be used. oldReferenceMapping.RemoveAt(i); found = true; break; } ++i; } if (!found) { // Otherwise, do a full update that will properly initialize the new reference. newReference.Value.Refresh(ownerNode, nodeContainer, nodeFactory, newReference.Key); } } } items = newReferences; // Remark: this works because both KeyCollection and List implements IReadOnlyCollection. Any internal change to HybridDictionary might break this! Indices = (IReadOnlyCollection <Index>)newReferences.Keys; } }
/// <summary> /// Creates a graph node. /// </summary> /// <param name="rootObject">The root object.</param> /// <param name="nodeFactory">The factory to use to create nodes.</param> /// <returns>A new graph node representing the given root object.</returns> private IGraphNode CreateNode(object rootObject, NodeFactoryDelegate nodeFactory) { if (rootObject == null) throw new ArgumentNullException(nameof(rootObject)); Guid guid = Guid.NewGuid(); // Retrieve results if (guidContainer != null && !rootObject.GetType().IsValueType) guid = guidContainer.GetOrCreateGuid(rootObject); var result = (GraphNode)NodeBuilder.Build(rootObject, guid, nodeFactory); if (result != null) { // Register the factory used to create this node. factoriesByNode.Add(result, nodeFactory); // Register reference objects nodesByGuid.Add(result.Guid, result); // Create or update nodes of referenced objects UpdateReferences(result); } return result; }
private void UpdateOrCreateReferenceTarget(IReference reference, IGraphNode node, NodeFactoryDelegate nodeFactory, Stack<object> indices = null) { if (reference == null) throw new ArgumentNullException(nameof(reference)); if (node == null) throw new ArgumentNullException(nameof(node)); var content = (ContentBase)node.Content; var referenceEnumerable = reference as ReferenceEnumerable; if (referenceEnumerable != null) { if (indices == null) indices = new Stack<object>(); foreach (var itemReference in referenceEnumerable) { indices.Push(itemReference.Index); UpdateOrCreateReferenceTarget(itemReference, node, nodeFactory, indices); indices.Pop(); } } else { if (content.ShouldProcessReference) { var singleReference = ((ObjectReference)reference); if (singleReference.TargetNode != null && singleReference.TargetNode.Content.Value != reference.ObjectValue) { singleReference.Clear(); } if (singleReference.TargetNode == null && reference.ObjectValue != null) { // This call will recursively update the references. var target = singleReference.SetTarget(this, nodeFactory); if (target != null) { var structContent = target.Content as BoxedContent; if (structContent != null) { structContent.BoxedStructureOwner = content; structContent.BoxedStructureOwnerIndices = indices?.Reverse().ToArray(); } } else { content.ShouldProcessReference = false; } } } } }
/// <summary> /// Set the <see cref="TargetNode"/> and <see cref="TargetGuid"/> of the targeted object by retrieving it from or creating it to the given <see cref="NodeContainer"/>. /// </summary> /// <param name="objectValue">The value for which to set the target node.</param> /// <param name="nodeContainer">The <see cref="NodeContainer"/> used to retrieve or create the target node.</param> /// <param name="nodeFactory">The factory to use to create nodes.</param> internal IGraphNode SetTarget(object objectValue, NodeContainer nodeContainer, NodeFactoryDelegate nodeFactory) { if (nodeContainer == null) { throw new ArgumentNullException(nameof(nodeContainer)); } IGraphNode targetNode = nodeContainer.GetOrCreateNodeInternal(objectValue, nodeFactory); SetTarget(targetNode); return(targetNode); }
/// <summary> /// Gets the node associated to a data object, if it exists, otherwise creates a new node for the object and its member recursively. /// </summary> /// <param name="rootObject">The data object.</param> /// <param name="nodeFactory">The factory to use to create nodes.</param> /// <returns>The <see cref="IGraphNode"/> associated to the given object.</returns> internal IGraphNode GetOrCreateNode(object rootObject, NodeFactoryDelegate nodeFactory) { if (nodeFactory == null) throw new ArgumentNullException(nameof(nodeFactory)); if (rootObject == null) return null; lock (lockObject) { IGraphNode result = null; if (guidContainer != null && !rootObject.GetType().IsValueType) { result = GetNode(rootObject); } return result ?? CreateNode(rootObject, nodeFactory); } }
public void Refresh(IGraphNode ownerNode, NodeContainer nodeContainer, NodeFactoryDelegate nodeFactory) { Refresh(ownerNode, nodeContainer, nodeFactory, Index.Empty); }
/// <summary> /// Set the <see cref="TargetNode"/> and <see cref="TargetGuid"/> of the targeted object by retrieving it from or creating it to the given <see cref="NodeContainer"/>. /// </summary> /// <param name="objectValue">The value for which to set the target node.</param> /// <param name="nodeContainer">The <see cref="NodeContainer"/> used to retrieve or create the target node.</param> /// <param name="nodeFactory">The factory to use to create nodes.</param> internal IGraphNode SetTarget(object objectValue, NodeContainer nodeContainer, NodeFactoryDelegate nodeFactory) { if (nodeContainer == null) throw new ArgumentNullException(nameof(nodeContainer)); IGraphNode targetNode = nodeContainer.GetOrCreateNodeInternal(objectValue, nodeFactory); SetTarget(targetNode); return targetNode; }
/// <summary> /// Gets the node associated to a data object, if it exists, otherwise creates a new node for the object and its member recursively. /// </summary> /// <param name="rootObject">The data object.</param> /// <param name="nodeFactory">The factory to use to create nodes.</param> /// <returns>The <see cref="IGraphNode"/> associated to the given object.</returns> internal IGraphNode GetOrCreateNodeInternal(object rootObject, NodeFactoryDelegate nodeFactory) { if (nodeFactory == null) throw new ArgumentNullException(nameof(nodeFactory)); if (rootObject == null) return null; lock (lockObject) { IGraphNode result; if (!rootObject.GetType().IsValueType) { result = GetNodeInternal(rootObject); if (result != null) return result; } var guid = !rootObject.GetType().IsValueType ? guidContainer.GetOrCreateGuid(rootObject) : Guid.NewGuid(); result = NodeBuilder.Build(rootObject, guid, nodeFactory); if (result != null) { // Register reference objects nodesByGuid.Add(result.Guid, result); // Create or update nodes of referenced objects UpdateReferencesInternal(result); } return result; } }