/// <summary> /// Event handler that is called when the document node has disposed or name changed. Because the path to the node can have changed too, /// the path is renewed in this case. The <see cref="OnChanged" /> method is called then for the proxy itself. /// </summary> /// <param name="sender"></param> /// <param name="source"></param> /// <param name="e"></param> private void EhDocNode_TunneledEvent(object sender, object source, Main.TunnelingEventArgs e) { if (IsDisposeInProgress) { return; } #if DEBUG_DOCNODEPROXYLOGGING Current.Console.WriteLine("RelDocNodeProxy.EhDocNode_TunneledEvent: sender={0}, source={1} e={2}", sender, source, e); #endif bool shouldFireChangedEvent = false; var senderAsNode = source as IDocumentLeafNode; if (!(senderAsNode != null)) { throw new InvalidProgramException(); } if (e is DisposeEventArgs) { // when our DocNode was disposed, it is probable that the parent of this node (and further parents) are disposed too // thus we need to watch the first node that is not disposed var docNode = InternalDocNode; ClearDocNode(); if (!(sender is AltaxoDocument)) // if the whole document is disposed, there is no point in trying to watch something { // note Dispose is designed to let the hierarchy from child to parent (root) valid, but not from root to child! // thus trying to get an actual document path here is in must cases unsuccessfull. We have to rely on our stored path, and that it was always updated! // the only case were it is successfull if a new node immediately replaces an old document node var node = RelativeDocumentPath.GetNodeOrLeastResolveableNode(_docNodePath, senderAsNode, out var wasResolvedCompletely); if (wasResolvedCompletely) { InternalSetDocNode(node, _parent); } else { SetWatchOnNode(node); } shouldFireChangedEvent = true; } } else if (e is DocumentPathChangedEventArgs) { if (null != InternalDocNode) { InternalDocumentPath = RelativeDocumentPath.GetRelativePathFromTo(_parent, InternalDocNode); } shouldFireChangedEvent = true; } if (shouldFireChangedEvent) { EhSelfChanged(EventArgs.Empty); } }
/// <summary> /// Sets the document node that is held by this proxy. /// </summary> /// <param name="value">The document node. If <c>docNode</c> implements <see cref="Main.IDocumentLeafNode" />, /// the document path is stored for this object in addition to the object itself.</param> /// <param name="parentNode">The start point of the document path. Should be equal to the member _parent, but this might be not set now.</param> protected void InternalSetDocNode(Main.IDocumentLeafNode value, IDocumentLeafNode parentNode) { if (!IsValidDocument(value)) { throw new ArgumentException("This type of document is not allowed for the proxy of type " + GetType().ToString()); } if (null == parentNode) { throw new InvalidOperationException("Parent of this node must be set in order to set the docnode."); } var oldValue = InternalDocNode; if (object.ReferenceEquals(oldValue, value)) { return; // Nothing to do } if (null != _weakDocNodeChangedHandler) { _weakDocNodeChangedHandler.Remove(); _weakDocNodeChangedHandler = null; } if (null != _weakDocNodeTunneledEventHandler) { _weakDocNodeTunneledEventHandler.Remove(); _weakDocNodeTunneledEventHandler = null; } if (null != oldValue) { ClearDocNode(); } var newPath = RelativeDocumentPath.GetRelativePathFromTo(parentNode, value); if (null != newPath) { InternalDocumentPath = newPath; // especially in dispose situations, the new path can be null. In this case we leave the path as it was } _docNodeRef = new WeakReference(value); #if DEBUG_DOCNODEPROXYLOGGING Current.Console.WriteLine("RelDocNodeProxy.SetDocNode, path is <<{0}>>", _docNodePath); #endif value.TunneledEvent += (_weakDocNodeTunneledEventHandler = new WeakActionHandler <object, object, TunnelingEventArgs>(EhDocNode_TunneledEvent, handler => value.TunneledEvent -= handler)); if (null != _docNodePath && !_docNodePath.IsIdentity) // it does not make sense to watch the changed event of our target node is our parent because the parent can handle the Changed event itself { value.Changed += (_weakDocNodeChangedHandler = new WeakEventHandler(EhDocNode_Changed, handler => value.Changed -= handler)); } OnAfterSetDocNode(); EhSelfChanged(new Main.InstanceChangedEventArgs(oldValue, value)); }
/// <summary> /// Event handler that is called when the watched node or a parent node below has disposed or its name changed. We then try to resolve the path again. /// </summary> /// <param name="sender"></param> /// <param name="source">Source of the tunneled event.</param> /// <param name="e"></param> private void EhWatchedNode_TunneledEvent(object sender, object source, Main.TunnelingEventArgs e) { if (IsDisposeInProgress) { return; } if (!(_docNodeRef == null)) { throw new InvalidProgramException(); } var senderAsDocNode = sender as IDocumentLeafNode; var sourceAsDocNode = source as IDocumentLeafNode; if (!(senderAsDocNode != null)) { throw new InvalidProgramException(); } if (!(sourceAsDocNode != null)) { throw new InvalidProgramException(); } // then we try to resolve the path again if ((e is DisposeEventArgs) || (e is DocumentPathChangedEventArgs)) { #if DEBUG_DOCNODEPROXYLOGGING Current.Console.WriteLine("DocNodeProxy.EhWatchedNode_TunneledEvent"); #endif var node = RelativeDocumentPath.GetNodeOrLeastResolveableNode(_docNodePath, sourceAsDocNode, out var wasResolvedCompletely); if (null == node) { throw new InvalidProgramException(nameof(node) + " should always be != null, since we use absolute paths, and at least an AltaxoDocument should be resolved here."); } if (wasResolvedCompletely) { ClearWatch(); InternalSetDocNode(node, _parent); } else // not completely resolved { if (!object.ReferenceEquals(sender, node)) { ClearWatch(); SetWatchOnNode(node); } } } }
public override void EhParentTunnelingEventHappened(IDocumentNode sender, IDocumentNode originalSource, TunnelingEventArgs e) { // since we deal with relative path, we have to watch changes to our path too if (e is Main.DocumentPathChangedEventArgs) { var node = InternalDocNode; if (null != node) { _docNodePath = RelativeDocumentPath.GetRelativePathFromTo(_parent, node); } } base.EhParentTunnelingEventHappened(sender, originalSource, e); }
/// <summary> /// Event handler that is called when the document node has changed. Because the path to the node can have changed too, /// the path is renewed in this case. The <see cref="OnChanged" /> method is called then for the proxy itself. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void EhDocNode_Changed(object sender, EventArgs e) { if (IsDisposeInProgress) { return; } #if DEBUG_DOCNODEPROXYLOGGING Current.Console.WriteLine("DocNodeProxy.EhDocNode_Changed: sender={0}, e={1}", sender, e); #endif if (null != InternalDocNode) { _docNodePath = RelativeDocumentPath.GetRelativePathFromTo(_parent, InternalDocNode); } EhSelfChanged(EventArgs.Empty); }
/// <summary> /// Gets the node that is designated by the provided <paramref name="path"/> or the least resolveable node. /// </summary> /// <param name="path">The document path to resolve.</param> /// <param name="startnode">The startnode.</param> /// <param name="pathWasCompletelyResolved">If set to <c>true</c> on return, the path was completely resolved. Otherwise, <c>false</c>.</param> /// <returns>The resolved node, or the least node on the path that could be resolved.</returns> /// <exception cref="System.ArgumentNullException"> /// path /// or /// startnode /// </exception> public static IDocumentLeafNode GetNodeOrLeastResolveableNode(RelativeDocumentPath path, IDocumentLeafNode startnode, out bool pathWasCompletelyResolved) { if (null == path) { throw new ArgumentNullException(nameof(path)); } if (null == startnode) { throw new ArgumentNullException(nameof(startnode)); } var node = startnode; var prevNode = startnode; for (int i = 0; i < path._numberOfLevelsDown && null != node; ++i) { prevNode = node; node = node.ParentNode; } if (null == node) { pathWasCompletelyResolved = false; return(prevNode); } pathWasCompletelyResolved = true; for (int i = 0; i < path._pathParts.Length && null != node; i++) { prevNode = node; if (node is Main.IDocumentNode) { node = ((Main.IDocumentNode)node).GetChildObjectNamed(path._pathParts[i]); } else { node = null; } } // end for pathWasCompletelyResolved = null != node; return(node ?? prevNode); }
/// <summary> /// Event handler that is called when the watched node (a node that is not the document node) has changed. Maybe this watched node had now created a parent node, and our /// document path can resolved now. That's why we try to resolve our document path now. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void EhWatchedNode_Changed(object sender, EventArgs e) { if (IsDisposeInProgress) { return; } #if DEBUG_DOCNODEPROXYLOGGING Current.Console.WriteLine("DocNodeProxy.EhWatchedNode_Changed: sender={0}, e={1}", sender, e); #endif if (!(_docNodeRef == null)) { throw new InvalidProgramException(); } var senderAsDocNode = sender as IDocumentLeafNode; if (!(senderAsDocNode != null)) { throw new InvalidProgramException(); } var node = RelativeDocumentPath.GetNodeOrLeastResolveableNode(_docNodePath, senderAsDocNode, out var wasResolvedCompletely); if (null == node) { throw new InvalidProgramException("node should always be != null, since we use absolute paths, and at least an AltaxoDocument should be resolved here."); } if (wasResolvedCompletely) { ClearWatch(); InternalSetDocNode(node, _parent); } else // not completely resolved { if (!object.ReferenceEquals(sender, node)) { ClearWatch(); SetWatchOnNode(node); } } }
public static IDocumentLeafNode GetObject(RelativeDocumentPath path, IDocumentLeafNode startnode) { if (null == path) { throw new ArgumentNullException(nameof(path)); } if (null == startnode) { throw new ArgumentNullException(nameof(startnode)); } var node = startnode; for (int i = 0; i < path._numberOfLevelsDown && null != node; ++i) { node = node.ParentNode; } if (null == node) { return(null); // Path not resolveable } for (int i = 0; i < path._pathParts.Length; i++) { if (node is Main.IDocumentNode) { node = ((Main.IDocumentNode)node).GetChildObjectNamed(path._pathParts[i]); } else { return(null); } } // end for return(node); }
public RelativeDocumentPath(RelativeDocumentPath from) : this(from._numberOfLevelsDown, from._pathParts) { }
/// <summary> /// Gets the node that is designated by the provided <paramref name="path"/> or the least resolveable node. /// </summary> /// <param name="path">The document path to resolve.</param> /// <param name="startnode">The startnode.</param> /// <param name="pathWasCompletelyResolved">If set to <c>true</c> on return, the path was completely resolved. Otherwise, <c>false</c>.</param> /// <returns>The resolved node, or the least node on the path that could be resolved.</returns> /// <exception cref="System.ArgumentNullException"> /// path /// or /// startnode /// </exception> public static IDocumentLeafNode GetNodeOrLeastResolveableNode(RelativeDocumentPath path, IDocumentLeafNode startnode, out bool pathWasCompletelyResolved) { if (null == path) throw new ArgumentNullException(nameof(path)); if (null == startnode) throw new ArgumentNullException(nameof(startnode)); var node = startnode; var prevNode = startnode; for (int i = 0; i < path._numberOfLevelsDown && null != node; ++i) { prevNode = node; node = node.ParentNode; } if (null == node) { pathWasCompletelyResolved = false; return prevNode; } pathWasCompletelyResolved = true; for (int i = 0; i < path._pathParts.Length && null != node; i++) { prevNode = node; if (node is Main.IDocumentNode) node = ((Main.IDocumentNode)node).GetChildObjectNamed(path._pathParts[i]); else node = null; } // end for pathWasCompletelyResolved = null != node; return node ?? prevNode; }
public static IDocumentLeafNode GetObject(RelativeDocumentPath path, IDocumentLeafNode startnode) { if (null == path) throw new ArgumentNullException(nameof(path)); if (null == startnode) throw new ArgumentNullException(nameof(startnode)); var node = startnode; for (int i = 0; i < path._numberOfLevelsDown && null != node; ++i) { node = node.ParentNode; } if (null == node) return null; // Path not resolveable for (int i = 0; i < path._pathParts.Length; i++) { if (node is Main.IDocumentNode) node = ((Main.IDocumentNode)node).GetChildObjectNamed(path._pathParts[i]); else return null; } // end for return node; }
/// <summary> /// Event handler that is called when the document node has disposed or name changed. Because the path to the node can have changed too, /// the path is renewed in this case. The <see cref="OnChanged" /> method is called then for the proxy itself. /// </summary> /// <param name="sender"></param> /// <param name="source"></param> /// <param name="e"></param> private void EhDocNode_TunneledEvent(object sender, object source, Main.TunnelingEventArgs e) { if (IsDisposeInProgress) return; #if DEBUG_DOCNODEPROXYLOGGING Current.Console.WriteLine("RelDocNodeProxy.EhDocNode_TunneledEvent: sender={0}, source={1} e={2}", sender, source, e); #endif bool shouldFireChangedEvent = false; var senderAsNode = source as IDocumentLeafNode; if (!(senderAsNode != null)) throw new InvalidProgramException(); if (e is DisposeEventArgs) { // when our DocNode was disposed, it is probable that the parent of this node (and further parents) are disposed too // thus we need to watch the first node that is not disposed var docNode = InternalDocNode; ClearDocNode(); if (!(sender is AltaxoDocument)) // if the whole document is disposed, there is no point in trying to watch something { // note Dispose is designed to let the hierarchy from child to parent (root) valid, but not from root to child! // thus trying to get an actual document path here is in must cases unsuccessfull. We have to rely on our stored path, and that it was always updated! // the only case were it is successfull if a new node immediately replaces an old document node bool wasResolvedCompletely; var node = RelativeDocumentPath.GetNodeOrLeastResolveableNode(_docNodePath, senderAsNode, out wasResolvedCompletely); if (wasResolvedCompletely) { InternalSetDocNode(node, _parent); } else { SetWatchOnNode(node); } shouldFireChangedEvent = true; } } else if (e is DocumentPathChangedEventArgs) { if (null != InternalDocNode) { InternalDocumentPath = RelativeDocumentPath.GetRelativePathFromTo(_parent, InternalDocNode); } shouldFireChangedEvent = true; } if (shouldFireChangedEvent) EhSelfChanged(EventArgs.Empty); }