/// <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. /// </summary> /// <param name="sender"></param> /// <param name="source">Source of the tunneled event.</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("DocNodeProxy.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 = InternalDocumentNode; ClearDocNode(); if (!(sender is IProject)) // if the whole project 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 = AbsoluteDocumentPath.GetNodeOrLeastResolveableNode(_docNodePath, senderAsNode, out var wasResolvedCompletely); if (wasResolvedCompletely) { SetDocNode(node); } else { SetWatchOnNode(node); } shouldFireChangedEvent = true; } } else if (e is DocumentPathChangedEventArgs) { if (null != InternalDocumentNode) { InternalDocumentPath = Main.AbsoluteDocumentPath.GetAbsolutePath(InternalDocumentNode); InternalCheckAbsolutePath(); } shouldFireChangedEvent = true; } if (shouldFireChangedEvent) { EhSelfChanged(EventArgs.Empty); } }
/// <summary> /// Replaces parts of the part by another part. /// </summary> /// <param name="partToReplace">Part of the path that should be replaced. This part has to match the beginning of this part. The last item of the part /// is allowed to be given only partially.</param> /// <param name="newPart">The new part to replace that piece of the path, that match the <c>partToReplace</c>.</param> /// <param name="newPath">The resulting new path (if the return value is <c>true</c>), or the original path, if the return value is <c>false</c>.</param> /// <returns>True if the part could be replaced. Returns false if the path does not fulfill the presumtions given above.</returns> /// <remarks> /// As stated above, the last item of the partToReplace can be given only partially. As an example, the path (here separated by space) /// <para>Tables Preexperiment1/WDaten Time</para> /// <para>should be replaced by </para> /// <para>Tables Preexperiment2\WDaten Time</para> /// <para>To make this replacement, the partToReplace should be given by</para> /// <para>Tables Preexperiment1/</para> /// <para>and the newPart should be given by</para> /// <para>Tables Preexperiment2\</para> /// <para>Note that Preexperiment1\ and Preexperiment2\ are only partially defined items of the path.</para> /// </remarks> public bool ReplacePathParts(AbsoluteDocumentPath partToReplace, AbsoluteDocumentPath newPart, out AbsoluteDocumentPath newPath) { newPath = this; // Test if the start of my path is identical to that of partToReplace if (Count < partToReplace.Count) { return(false); } for (int i = 0; i < partToReplace.Count - 1; i++) { if (0 != string.Compare(this[i], partToReplace[i])) { return(false); } } if (!this[partToReplace.Count - 1].StartsWith(partToReplace[partToReplace.Count - 1])) { return(false); } // ok, the beginning of my path and partToReplace is identical, // thus we replace the beginning of my path with that of newPart var list = new List <string>(_pathParts); string s = list[partToReplace.Count - 1].Substring(partToReplace[partToReplace.Count - 1].Length); list[partToReplace.Count - 1] = newPart[newPart.Count - 1] + s; int j, k; for (j = partToReplace.Count - 2, k = newPart.Count - 2; j >= 0 && k >= 0; --j, --k) { list[j] = newPart[k]; } if (k > 0) { for (; k >= 0; --k) { list.Insert(0, newPart[k]); } } else if (j > 0) { for (; j >= 0; --j) { list.RemoveAt(0); } } newPath = new AbsoluteDocumentPath(list.ToArray()); return(true); }
/// <summary> /// Resolves the path by first using startnode as starting point. If that failed, try using documentRoot as starting point. /// </summary> /// <param name="path">The path to resolve.</param> /// <param name="startnode">The node object which is considered as the starting point of the path.</param> /// <param name="documentRoot">An alternative node which is used as starting point of the path if the first try failed.</param> /// <returns>The resolved object. If the resolving process failed, the return value is null.</returns> public static IDocumentLeafNode GetObject(AbsoluteDocumentPath path, IDocumentLeafNode startnode, IDocumentNode documentRoot) { var retval = GetObject(path, startnode); if (null == retval && null != documentRoot) { retval = GetObject(path, documentRoot); } return(retval); }
private void SortItemsByDependencyProxyReporter(IProxy proxy, object owner, string propertyName, HashSet <IProjectItem> dependenciesOfItem) { var proxyDoc = proxy?.DocumentObject as IDocumentLeafNode; if (proxyDoc != null) { var dependentOnItem = AbsoluteDocumentPath.GetRootNodeImplementing <IProjectItem>(proxyDoc); if (null != dependentOnItem) { dependenciesOfItem.Add(dependentOnItem); } } }
/// <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(AbsoluteDocumentPath path, IDocumentLeafNode startnode, out bool pathWasCompletelyResolved) { if (null == path) { throw new ArgumentNullException("path"); } if (null == startnode) { throw new ArgumentNullException("startnode"); } var node = startnode; node = GetRootNode(node); if (null == node) { throw new InvalidProgramException("startnote is not rooted"); } var prevNode = node; pathWasCompletelyResolved = true; for (int i = 0; i < path.Count; i++) { prevNode = node; if (path[i] == "..") { node = node.ParentObject; } else { if (node is Main.IDocumentNode) { node = ((Main.IDocumentNode)node).GetChildObjectNamed(path[i]); } else { node = null; } } if (node == null) { pathWasCompletelyResolved = false; break; } } // end for return(node ?? prevNode); }
public AbsoluteDocumentPath Append(AbsoluteDocumentPath other) { if (null == other) { throw new ArgumentNullException("other"); } var arr = new string[_pathParts.Length + other._pathParts.Length]; Array.Copy(_pathParts, arr, _pathParts.Length); Array.Copy(other._pathParts, 0, arr, _pathParts.Length, other._pathParts.Length); return(new AbsoluteDocumentPath(arr)); }
public bool StartsWith(AbsoluteDocumentPath another) { if (Count < another.Count) { return(false); } for (int i = 0; i < another.Count; ++i) { if (this[i] != another[i]) { return(false); } } return(true); }
/// <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 (!(InternalDocumentNode == null)) { throw new InvalidProgramException(); } var senderAsDocNode = sender as IDocumentLeafNode; if (!(senderAsDocNode != null)) { throw new InvalidProgramException(); } var node = AbsoluteDocumentPath.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(); SetDocNode(node); } else // not completely resolved { if (!object.ReferenceEquals(sender, node)) { ClearWatch(); SetWatchOnNode(node); } } }
public static IDocumentLeafNode GetObject(AbsoluteDocumentPath path, IDocumentLeafNode startnode) { if (null == path) { throw new ArgumentNullException("path"); } if (null == startnode) { throw new ArgumentNullException("startnode"); } var node = startnode; node = GetRootNode(node); for (int i = 0; i < path.Count; i++) { if (path[i] == "..") { if (null != node) { node = node.ParentObject; } else { return(null); } } else { if (node is Main.IDocumentNode) { node = ((Main.IDocumentNode)node).GetChildObjectNamed(path[i]); } else { return(null); } } } // end for return(node); }
/// <summary> /// Replaces parts of the part of the document node by another part. If the replacement was successful, the original document node is cleared. /// See <see cref="M:DocumentPath.ReplacePathParts"/> for details of the part replacement. /// </summary> /// <param name="partToReplace">Part of the path that should be replaced. This part has to match the beginning of this part. The last item of the part /// is allowed to be given only partially.</param> /// <param name="newPart">The new part to replace that piece of the path, that match the <c>partToReplace</c>.</param> /// <param name="rootNode">Any document node in the hierarchy that is used to find the root node of the hierarchy.</param> /// <returns>True if the path could be replaced. Returns false if the path does not fulfill the presumptions given above.</returns> /// <remarks> /// As stated above, the last item of the partToReplace can be given only partially. As an example, the path (here separated by space) /// <para>Tables Preexperiment1/WDaten Time</para> /// <para>should be replaced by </para> /// <para>Tables Preexperiment2\WDaten Time</para> /// <para>To make this replacement, the partToReplace should be given by</para> /// <para>Tables Preexperiment1/</para> /// <para>and the newPart should be given by</para> /// <para>Tables Preexperiment2\</para> /// <para>Note that Preexperiment1\ and Preexperiment2\ are only partially defined items of the path.</para> /// </remarks> public bool ReplacePathParts(AbsoluteDocumentPath partToReplace, AbsoluteDocumentPath newPart, IDocumentLeafNode rootNode) { if (!(_docNodePath != null || IsDisposeInProgress)) { throw new InvalidProgramException(); } if (null == rootNode) { throw new ArgumentNullException(nameof(rootNode)); } var success = _docNodePath.ReplacePathParts(partToReplace, newPart, out var newPath); if (success) { _docNodePath = newPath; ClearDocNode(); ResolveDocumentObject(rootNode); } return(success); }
public static RelativeDocumentPath FromOldDeprecated(AbsoluteDocumentPath absPath) { if (null == absPath) { throw new ArgumentNullException(nameof(absPath)); } int numberOfLevelsDown = 0; var pathList = new List <string>(); for (int i = 0; i < absPath.Count; ++i) { if (absPath[i] == "..") { numberOfLevelsDown++; } else { pathList.Add(absPath[i]); } } return(new RelativeDocumentPath(numberOfLevelsDown, pathList)); }
/// <summary> /// Get the absolute path of the node <code>node</code> starting from the root. /// </summary> /// <param name="node">The node for which the path is retrieved.</param> /// <returns>The absolute path of the node. The first element is a "/" to mark this as absolute path.</returns> public static AbsoluteDocumentPath GetAbsolutePath(IDocumentLeafNode node) { AbsoluteDocumentPath path = GetPath(node, int.MaxValue); return(path); }
/// <summary> /// Adds a replacement entry for a project item. The paths given should be complete paths of the project item (original path and new path). /// </summary> /// <param name="originalPath">The original path of the project item (DataTable, Graph, ProjectFolderPropertyBag).</param> /// <param name="newPath">The new path of the project item.</param> public void AddProjectItemReplacement(AbsoluteDocumentPath originalPath, AbsoluteDocumentPath newPath) { _itemRelocationDictionary.Add(originalPath, newPath); }
/// <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 (!(InternalDocumentNode == 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(); } if (e is DocumentPathChangedEventArgs) // here, we activly change our stored path, if the watched node or a parent has changed its name { var watchedPath = AbsoluteDocumentPath.GetAbsolutePath(senderAsDocNode); watchedPath = watchedPath.Append(_docNodePath.SubPath(watchedPath.Count, _docNodePath.Count - watchedPath.Count)); var oldPath = _docNodePath; _docNodePath = watchedPath; #if DEBUG_DOCNODEPROXYLOGGING Current.Console.WriteLine("DocNodeProxy.EhWatchedNode_TunneledEvent: Modified path, oldpath={0}, newpath={1}", oldPath, _docNodePath); #endif } // 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 = AbsoluteDocumentPath.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(); SetDocNode(node); } else // not completely resolved { if (!object.ReferenceEquals(sender, node)) { ClearWatch(); SetWatchOnNode(node); } } } }
/// <summary> /// Sets the watch on a node that is not our document node, but a node lower in the hierarchy. We watch both the Changed event and the TunneledEvent of this node. /// </summary> /// <param name="node">The node to watch.</param> protected virtual void SetWatchOnNode(IDocumentLeafNode node) { #if DOCNODEPROXY_CONCURRENTDEBUG int debugUsn = System.Threading.Interlocked.Increment(ref _debugUSN); _debug.Enqueue("START SetWatchOnNode " + debugUsn.ToString()); #endif if (null == node) { throw new ArgumentNullException(nameof(node)); } if (null != _weakDocNodeChangedHandler) { _weakDocNodeChangedHandler.Remove(); _weakDocNodeChangedHandler = null; } if (null != _weakDocNodeTunneledEventHandler) { _weakDocNodeTunneledEventHandler.Remove(); _weakDocNodeTunneledEventHandler = null; } node.TunneledEvent += (_weakDocNodeTunneledEventHandler = new WeakActionHandler <object, object, TunnelingEventArgs>(EhWatchedNode_TunneledEvent, handler => node.TunneledEvent -= handler)); node.Changed += (_weakDocNodeChangedHandler = new WeakEventHandler(EhWatchedNode_Changed, handler => node.Changed -= handler)); #if DEBUG_DOCNODEPROXYLOGGING Current.Console.WriteLine("Start watching node <<{0}>> of total path <<{1}>>", AbsoluteDocumentPath.GetAbsolutePath(node), _docNodePath); #endif #if DOCNODEPROXY_CONCURRENTDEBUG _debug.Enqueue("STOP SetWatchOnNode " + debugUsn.ToString() + (_docNodeRef == null).ToString()); #endif }
/// <summary> /// Sets the watch on a node that is not our document node, but a node lower in the hierarchy. We watch both the Changed event and the TunneledEvent of this node. /// </summary> /// <param name="node">The node to watch.</param> protected virtual void SetWatchOnNode(IDocumentLeafNode node) { if (null == node) { throw new ArgumentNullException(nameof(node)); } if (null != _weakDocNodeChangedHandler) { _weakDocNodeChangedHandler.Remove(); _weakDocNodeChangedHandler = null; } if (null != _weakDocNodeTunneledEventHandler) { _weakDocNodeTunneledEventHandler.Remove(); _weakDocNodeTunneledEventHandler = null; } node.TunneledEvent += (_weakDocNodeTunneledEventHandler = new WeakActionHandler <object, object, TunnelingEventArgs>(EhWatchedNode_TunneledEvent, handler => node.TunneledEvent -= handler)); node.Changed += (_weakDocNodeChangedHandler = new WeakEventHandler(EhWatchedNode_Changed, handler => node.Changed -= handler)); #if DEBUG_DOCNODEPROXYLOGGING Current.Console.WriteLine("Start watching node <<{0}>> of total path <<{1}>>", AbsoluteDocumentPath.GetAbsolutePath(node), _docNodePath); #endif }
public AbsoluteDocumentPath(AbsoluteDocumentPath from) { _pathParts = (string[])from._pathParts.Clone(); }