/// <summary> /// Advances the parser until the specified object is closed i.e. has a closing tag. /// </summary> /// <param name="parser">A spine parser. Its state will be modified.</param> /// <param name="ob">The object to complete</param> /// <param name="text">The text snapshot corresponding to the parser.</param> /// <param name="maximumReadahead">Maximum number of characters to advance before giving up.</param> /// <returns>Whether the object was successfully completed</returns> public static bool AdvanceUntilClosed(this XmlSpineParser parser, XObject ob, ITextSource text, int maximumReadahead = DEFAULT_READAHEAD_LIMIT) { var el = ob as XElement; if (el == null) { return(AdvanceUntilEnded(parser, ob, text, maximumReadahead)); } var startingDepth = parser.Spine.Count; var end = Math.Min(text.Length - parser.Position, maximumReadahead) + parser.Position; while (parser.Position < end) { parser.Push(text[parser.Position]); if (el.IsClosed) { return(true); } // just in case, bail if we pop out past the element's parent if (parser.Spine.Count < startingDepth - 1) { return(false); } } return(false); }
/// <summary> /// Advances the parser until the specified object is ended. /// </summary> /// <param name="parser">A spine parser. Its state will be modified.</param> /// <param name="ob">The object to complete</param> /// <param name="text">The text snapshot corresponding to the parser.</param> /// <param name="maximumReadahead">Maximum number of characters to advance before giving up.</param> /// <returns>Whether the object was successfully completed</returns> public static bool AdvanceUntilEnded(this XmlSpineParser parser, XObject ob, ITextSource text, int maximumReadahead = DEFAULT_READAHEAD_LIMIT) { var startingDepth = parser.Spine.Count; var end = Math.Min(text.Length - parser.Position, maximumReadahead) + parser.Position; while (parser.Position < end) { parser.Push(text[parser.Position]); if (ob.IsEnded) { return(true); } if (parser.Spine.Count < startingDepth) { return(false); } } // if at end of document, consider text nodes to be ended anyways if (parser.Position == text.Length && ob is XText xt) { xt.End(text.GetTextBetween(xt.Span.Start, text.Length)); } return(false); }
/// <summary> /// Advances the parser to end the node at the current position and gets that node's path. /// </summary> /// <param name="parser">A spine parser. Its state will be modified.</param> /// <param name="text">The text snapshot corresponding to the parser.</param> /// <returns></returns> public static List <XObject> AdvanceToNodeEndAndGetNodePath(this XmlSpineParser parser, ITextSource text, int maximumReadahead = DEFAULT_READAHEAD_LIMIT) { var context = parser.GetContext(); int startOffset = parser.Position; int startDepth = parser.Spine.Count; //if in potential start of a state, advance into the next state var end = Math.Min(text.Length - context.Position, maximumReadahead) + context.Position; if (parser.Position < end && (XmlRootState.IsNotFree(context) || (context.CurrentState is XmlRootState && text[parser.Position] == '<'))) { do { parser.Push(text[parser.Position]); } while (parser.Position < end && XmlRootState.IsNotFree(context)); //if it transitioned to another state, eat until we get a new node on the stack if (parser.Position < end && !(context.CurrentState is XmlRootState) && context.Nodes.Count <= startDepth) { parser.Push(text[parser.Position]); } } var path = parser.Spine.ToNodePath(); // make sure the leaf node is ended if (path.Count > 0) { var leaf = path[path.Count - 1]; if (!(leaf is XDocument)) { AdvanceUntilEnded(parser, leaf, text, maximumReadahead - (parser.Position - startOffset)); } //the leaf node might have a child that's a better match for the offset if (leaf is XContainer c && c.FindAtOffset(startOffset) is XObject o) { path.Add(o); } } return(path); }