internal ObjectXPathNavigator( object obj, ObjectXPathContext context ) { _context = context; _root = new Node( _context, new RootNodePolicy( obj ) ); _node = _root; if( _context.DetectLoops ) { _navigationStack = new Stack(); _navigationStack.Push( _root ); // Push dummy root object } _lang = _context.NameTable.Add( "" ); }
/// <summary> /// See <see cref="System.Xml.XPath.XPathNavigator.MoveToPrevious" /> for details. /// </summary> public override bool MoveToPrevious() { #if DEBUG Trace( "MoveToPrevious" ); #endif // Invoke child navigator if( _childNav != null ) { bool res = _childNav.MoveToPrevious(); if( res ) return true; if( !_node.IsTransparent || _childNavDepth > 0 ) return false; } var newnode = GetNonTransparentSibling( _node, false ); while( newnode != null ) { if( !newnode.IsTransparent ) { _node = newnode; _childNav = null; if( _context.DetectLoops ) { Trace( () => string.Format( "Move2Prev Pushing {0} {1}", _node.Object, GetNavigationStack() ) ); if( _navigationStack.Count > 0 ) _navigationStack.Pop(); else Trace( () => string.Format( "{0} MoveToPrevious : stack empty", Object ) ); _navigationStack.Push( newnode.Object ); } return true; } // It's transparent but navigable node. // Try to move into this navigable node. if( MoveIntoNavigableNode( newnode ) ) { _node = newnode; // Go to the last sibling of the child navigator's node while( _childNav.MoveToNext() ) ; if( _context.DetectLoops ) { Trace( () => string.Format( "Move2Prev {0} {1}", _node.Object, GetNavigationStack() ) ); if( _navigationStack.Count > 0 ) _navigationStack.Pop(); else Trace( () => string.Format( "{0} MoveToPrevious : stack is empty", Object ) ); _navigationStack.Push( newnode.Object ); } return true; } // If failure, get its sibling newnode = GetNonTransparentSibling( newnode, false ); continue; } return false; }
/// <summary> /// See <see cref="System.Xml.XPath.XPathNavigator.MoveToParent" /> for details. /// </summary> public override bool MoveToParent() { #if DEBUG Trace( "MoveToParent" ); Trace( GetNavigationStack ); #endif // Invoke child navigator if( _childNav != null ) { Trace( () => String.Format( "Trace: ChildNav" ) ); if( _childNavDepth == 0 ) { // We reached root node of the child navigator _childNav = null; if( !_node.IsTransparent ) // Child navigator cleared, we are at its non-transparent container return true; } else { // Descend one level _childNav.MoveToParent(); _childNavDepth--; return true; } } var parent = GetNonTransparentParent( _node ); if( _context.DetectLoops ) if( parent != null ) { Trace( () => String.Format( "Move2Parent popping {0}", GetNavigationStack() ) ); if( _navigationStack.Count > 0 ) _navigationStack.Pop(); else Trace( () => String.Format( "MoveToParent: Stack is empty" ) ); } if( parent != null ) { _node = parent; return true; } return false; }
/// <summary> /// See <see cref="System.Xml.XPath.XPathNavigator.MoveToNextAttribute" /> for details. /// </summary> public override bool MoveToNextAttribute() { #if DEBUG Trace( "MoveToNextAttribute" ); #endif // Invoke child navigator if( _childNav != null ) return _childNav.MoveToNextAttribute(); if( _node.NodeType != XPathNodeType.Attribute ) return false; for( int i = _node.Index + 1; i < _node.Parent.AttributesCount; i++ ) { var newnode = _node.Parent.GetAttribute( i ); if( newnode != null && !newnode.IsTransparent ) { _node = newnode; if( _context.DetectLoops ) { Trace( () => string.Format( "Move2NextAttr popping {0}", GetNavigationStack() ) ); if( _navigationStack.Count > 0 ) _navigationStack.Pop(); else Trace( () => string.Format( "MoveToNextAttribute: Stack is empty {0}", GetNavigationStack() ) ); _navigationStack.Push( newnode.Object ); } return true; } } Trace( () => string.Format( "~Move2NextAttr" ) ); return false; }
/// <summary> /// See <see cref="System.Xml.XPath.XPathNavigator.MoveToFirstChild" /> for details. /// </summary> public override bool MoveToFirstChild() { #if DEBUG Trace( "MoveToFirstChild" ); #endif // Invoke child navigator if( _childNav != null ) { bool res = _childNav.MoveToFirstChild(); if( res ) _childNavDepth++; return res; } // Check if we are trying to descend into IXPathNavigable object if( IsNodeXPathNavigable( _node ) ) return MoveIntoNavigableNode( _node ); var newnode = GetNonTransparentChild( _node, true ); while( newnode != null ) { if( !newnode.IsTransparent ) { _node = newnode; if( _context.DetectLoops ) { Trace( () => string.Format( "MoveTo1stChild Pushing {0} {1} TRUE", _node.Object, GetNavigationStack() ) ); _navigationStack.Push( newnode.Object ); } return true; } // It's transparent but navigable node. // Try to move into this navigable node. if( MoveIntoNavigableNode( newnode ) ) { _node = newnode; if( _context.DetectLoops ) { Trace( () => string.Format( "T: MoveTo1stChild Pushing {0} {1} TRUE", _node.Object, GetNavigationStack() ) ); _navigationStack.Push( newnode.Object ); } return true; } // If failure, get its sibling newnode = GetNonTransparentSibling( newnode, true ); continue; } return false; }
/// <summary> /// See <see cref="System.Xml.XPath.XPathNavigator.MoveToFirstAttribute" /> for details. /// </summary> public override bool MoveToFirstAttribute() { #if DEBUG Trace( "MoveToFirstAttribute" ); #endif // Invoke child navigator if( _childNav != null ) { bool res = _childNav.MoveToFirstAttribute(); if( res ) _childNavDepth++; return res; } if( _node.AttributesCount == 0 ) return false; for( int i = 0; i < _node.AttributesCount; i++ ) { var newnode = _node.GetAttribute( i ); if( newnode != null && !newnode.IsTransparent ) { _node = newnode; if( _context.DetectLoops ) { _navigationStack.Push( _node.Object ); Trace( () => string.Format( "Move2.1stAttr Pushed {0} {1}", _node.Object, GetNavigationStack() ) ); } return true; } } return false; }
/// <summary> /// Adds the child to this node. /// </summary> /// <param name="child">Child node to add.</param> /// <remarks>This method should be used with caution, because it could /// cause interference with child nodes returned by the node policy.</remarks> public void AddChild( Node child ) { int index = ChildrenCount; CacheChild( index, child ); child.Parent = this; child.Index = index; _elementsCount = ChildrenCount + 1; }
private static bool IsNodeXPathNavigable( Node node ) { if( node.ObjectType == null ) return false; return node.ObjectType.IsXPathNavigable; }
private Node GetNonTransparentSibling( Node current, bool goForward ) { if( current.NodeType == XPathNodeType.Attribute ) return null; Node newnode; var parent = current.Parent; if( parent == null ) return null; Object old = null; if( _context.DetectLoops ) if( _navigationStack.Count > 0 ) old = _navigationStack.Pop(); do { newnode = goForward ? parent.GetChild( current.Index + 1 ) : parent.GetChild( current.Index - 1 ); // While there are no more nodes in this transparent parent, try to // ascend up the hierarchy and take node next to this transparent node. while( newnode == null && parent.IsTransparent ) { newnode = parent; parent = parent.Parent; newnode = goForward ? parent.GetChild( newnode.Index + 1 ) : parent.GetChild( newnode.Index - 1 ); } // If node is found but it is transparent and not navigable if( newnode != null && newnode.IsTransparent && !IsNodeXPathNavigable( newnode ) ) { current = newnode; newnode = GetNonTransparentChild( current, goForward ); if( newnode != null ) break; continue; } // If we have to detect loops if( _context.DetectLoops ) if( newnode != null && newnode.Object != null && _navigationStack.Contains( newnode.Object ) ) { var evt = new LoopDetectionEventArgs( this, newnode ); _context.OnLoopDetected( evt ); if( !evt.IgnoreLoop ) { current = newnode; continue; } } break; } while( true ); if( old != null ) _navigationStack.Push( old ); return newnode; }
private bool MoveIntoNavigableNode( Node node ) { var navigable = node.Object as IXPathNavigable; if( navigable != null ) { _childNav = navigable.CreateNavigator(); _childNavDepth = 0; if( _childNav.NodeType == XPathNodeType.Root || ( node.Member != null && node.Member.SkipNavigableRoot != null ) ) { bool res = _childNav.MoveToFirstChild(); if( !res ) { // The type of node is Root and we can't descend to a child _childNav = null; return false; } _childNav.MoveToFirst(); } // Descended succesfully return true; } return false; }
private void CheckCache( int elemIndex ) { int elCount = ChildrenCount; if( _cache == null ) { int size = AttributesCount; if( elCount == int.MaxValue || elCount <= elemIndex ) if( elemIndex >= MinimalElementsCacheSize ) size += elemIndex + 1; else // Initial elements cache size for unknown number of children size += MinimalElementsCacheSize; else size += elCount; _cache = size != 0 ? new Node[size] : EmptyCache; } else if( elCount == int.MaxValue || elCount <= elemIndex ) { // Check if cache has to be expanded int size = _cache.Length; int elemSize = size - AttributesCount; if( elemIndex >= elemSize ) { var newCache = new Node[size + elemSize]; Array.Copy( _cache, 0, newCache, 0, size ); } } }
/// <summary> /// Caches child element. /// </summary> /// <param name="i">Zero-based index of child element.</param> /// <param name="child">Child element to add.</param> private void CacheChild( int i, Node child ) { CheckCache( i ); _cache[AttributesCount + i] = ( child ?? NullCacheEntry ); }
/// <summary> /// Caches attribute. /// </summary> /// <param name="i">Zero-based index of attribute.</param> /// <param name="child">Attribute to add..</param> private void CacheAttribute( int i, Node child ) { CheckCache( 0 ); _cache[i] = ( child ?? NullCacheEntry ); }
/// <summary> /// See <see cref="System.Xml.XPath.XPathNavigator.MoveToRoot" /> for details. /// </summary> public override void MoveToRoot() { #if DEBUG Trace( "MoveToRoot" ); #endif _node = _root; _childNav = null; if( _context.DetectLoops ) { _navigationStack.Clear(); Trace( () => string.Format( "Move2Root {0} {1}", _node.Object, GetNavigationStack() ) ); } }
/// <summary> /// See <see cref="System.Xml.XPath.XPathNavigator.MoveTo" /> for details. /// </summary> public override bool MoveTo( XPathNavigator other ) { var otherNav = other as ObjectXPathNavigator; if( otherNav == null ) return false; _context = otherNav._context; _root = otherNav._root; _node = otherNav._node; if( otherNav._childNav != null ) { _childNav = otherNav._childNav.Clone(); _childNavDepth = otherNav._childNavDepth; } else _childNav = null; #if DEBUG Trace( () => string.Format( "MoveTo( N#{0} )", other.GetHashCode() ) ); #endif if( _context.DetectLoops ) _navigationStack = (Stack)otherNav._navigationStack.Clone(); return true; }
private static Node GetNonTransparentParent( Node node ) { var parent = node.Parent; while( parent != null && parent.IsTransparent ) parent = parent.Parent; return parent; }
/// <summary> /// See <see cref="System.Xml.XPath.XPathNavigator.MoveToAttribute" /> for details. /// </summary> public override bool MoveToAttribute( string localName, string namespaceURI ) { #if DEBUG Trace( "MoveToAttribute" ); #endif // Invoke child navigator if( _childNav != null ) { bool res = _childNav.MoveToAttribute( localName, namespaceURI ); if( res ) _childNavDepth++; return res; // TODO: Check what will happen if we are positioned at the navigable root of the transparent node. } if( _node.NodeType != XPathNodeType.Element ) return false; int i = _node.FindAttribute( localName, namespaceURI ); if( i >= 0 ) { var newnode = _node.GetAttribute( i ); if( newnode != null && !newnode.IsTransparent ) { _node = newnode; return true; } } return false; }
private Node GetNonTransparentChild( Node current, bool goForward ) { if( current.ChildrenCount == 0 ) return null; Node newNode; bool inStack = false; int index = goForward ? 0 : current.ChildrenCount - 1; do { newNode = current.GetChild( index ); if( newNode == null && current.IsTransparent ) return GetNonTransparentSibling( current, goForward ); // If we have to detect loops. if( _context.DetectLoops ) if( newNode != null && newNode.Object != null && _navigationStack.Contains( newNode.Object ) ) { var evt = new LoopDetectionEventArgs( this, newNode ); _context.OnLoopDetected( evt ); if( !evt.IgnoreLoop ) { inStack = true; Trace( () => String.Format( "GetNonTransparentChild {0} already in stack", newNode.Object ) ); if( goForward ) index++; else index--; continue; } } else inStack = false; current = newNode; if( current != null ) index = goForward ? 0 : current.ChildrenCount - 1; } while( inStack || ( current != null && current.IsTransparent && !IsNodeXPathNavigable( current ) ) ); // Break if current is null, or non trasparent or navigable return current; }
/// <summary> /// Creates a new <see cref="LoopDetectionEventArgs"/> instance. /// </summary> /// <param name="navigator"><see cref="ObjectXPathNavigator"/> Navigator object</param> /// <param name="node"><see cref="Node"/> Node that caused loop appearance</param> public LoopDetectionEventArgs( ObjectXPathNavigator navigator, Node node ) { _navigator = navigator; _node = node; _ignoreLoop = false; }