/// <summary> /// Parses a Knockout begin containerless comment /// </summary> /// <param name="commentText">Comment text</param> /// <param name="expressionHandler">Binding expression handler</param> public static void ParseBeginContainerlessComment(string commentText, ExpressionDelegate expressionHandler) { Match koBeginContainerlessCommentMatch = _koBeginContainerlessCommentRegex.Match(commentText); if (koBeginContainerlessCommentMatch.Success) { var innerContext = new InnerMarkupParsingContext(commentText); var context = new MarkupParsingContext(innerContext); Group expressionGroup = koBeginContainerlessCommentMatch.Groups["expression"]; int expressionPosition = expressionGroup.Index; string expression = expressionGroup.Value.TrimEnd(); innerContext.IncreasePosition(expressionPosition); if (expressionHandler != null) { expressionHandler(context, expression); } } }
/// <summary> /// Parses XML content /// </summary> /// <param name="content">XML content</param> public void Parse(string content) { int contentLength = content.Length; if (contentLength == 0) { return; } lock (_parsingSynchronizer) { _innerContext = new InnerMarkupParsingContext(content); _context = new MarkupParsingContext(_innerContext); int endPosition = contentLength - 1; int previousPosition = -1; try { while (_innerContext.Position <= endPosition) { bool isProcessed = false; int firstCharPosition = _innerContext.Position; char firstCharValue; bool firstCharExist = content.TryGetChar(firstCharPosition, out firstCharValue); if (firstCharExist && firstCharValue == '<') { int secondCharPosition = firstCharPosition + 1; char secondCharValue; bool secondCharExist = content.TryGetChar(secondCharPosition, out secondCharValue); if (secondCharExist) { if (IsTagFirstChar(secondCharValue)) { // Start tag isProcessed = ProcessStartTag(); } else { int thirdCharPosition = secondCharPosition + 1; char thirdCharValue; bool thirdCharExist = content.TryGetChar(thirdCharPosition, out thirdCharValue); if (thirdCharExist) { switch (secondCharValue) { case '/': if (IsTagFirstChar(thirdCharValue)) { // End tag isProcessed = ProcessEndTag(); } break; case '!': switch (thirdCharValue) { case '-': int fourthCharPosition = thirdCharPosition + 1; char fourthCharValue; bool fourthCharExist = content.TryGetChar( fourthCharPosition, out fourthCharValue); if (fourthCharExist && fourthCharValue == '-') { // XML comments isProcessed = ProcessComment(); } break; case '[': // CDATA sections isProcessed = ProcessCdataSection(); break; case 'D': case 'd': // Doctype declaration isProcessed = ProcessDoctype(); break; } break; case '?': // XML declaration and processing instructions isProcessed = ProcessProcessingInstruction(); break; } } } } } if (!isProcessed) { // Text ProcessText(); } if (_innerContext.Position == previousPosition) { throw new MarkupParsingException( string.Format(Strings.ErrorMessage_MarkupParsingFailed, "XML"), _innerContext.NodeCoordinates, _innerContext.GetSourceFragment()); } previousPosition = _innerContext.Position; } // Check whether there were not closed tags if (_tagStack.Count > 0) { StackedXmlTag stackedTag = _tagStack.Pop(); throw new MarkupParsingException( string.Format(Strings.ErrorMessage_NotClosedTag, stackedTag.Name), stackedTag.Coordinates, SourceCodeNavigator.GetSourceFragment(_innerContext.SourceCode, stackedTag.Coordinates)); } } catch (MarkupParsingException) { throw; } finally { _tagStack.Clear(); _context = null; _innerContext = null; } } }
/// <summary> /// Parses a Angular comment directive /// </summary> /// <param name="commentText">Comment text</param> /// <param name="directiveNameHandler">Directive name handler</param> /// <param name="expressionHandler">Binding expression handler</param> public static void ParseCommentDirective(string commentText, DirectiveNameDelegate directiveNameHandler, ExpressionDelegate expressionHandler) { Match ngCommentDirectiveMatch = _ngCommentDirectiveRegex.Match(commentText); if (ngCommentDirectiveMatch.Success) { var innerContext = new InnerMarkupParsingContext(commentText); var context = new MarkupParsingContext(innerContext); GroupCollection groups = ngCommentDirectiveMatch.Groups; Group directiveNameGroup = groups["directiveName"]; int directiveNamePosition = directiveNameGroup.Index; string originalDirectiveName = directiveNameGroup.Value; string normalizedDirectiveName = NormalizeDirectiveName(originalDirectiveName); innerContext.IncreasePosition(directiveNamePosition); if (directiveNameHandler != null) { directiveNameHandler(context, originalDirectiveName, normalizedDirectiveName); } Group expressionGroup = groups["expression"]; if (expressionGroup.Success) { int expressionPosition = expressionGroup.Index; string expression = expressionGroup.Value.Trim(); innerContext.IncreasePosition(expressionPosition - directiveNamePosition); if (expressionHandler != null) { expressionHandler(context, expression); } } } }
/// <summary> /// Parses a Angular class directive /// </summary> /// <param name="className">Class name</param> /// <param name="directiveNameHandler">Directive name handler</param> /// <param name="expressionHandler">Binding expression handler</param> /// <param name="semicolonHandler">Semicolon handler</param> public static void ParseClassDirective(string className, DirectiveNameDelegate directiveNameHandler, ExpressionDelegate expressionHandler, SemicolonDelegate semicolonHandler) { MatchCollection ngClassDirectiveMatches = _ngClassDirectiveRegex.Matches(className); if (ngClassDirectiveMatches.Count > 0) { var innerContext = new InnerMarkupParsingContext(className); var context = new MarkupParsingContext(innerContext); int currentPosition = 0; foreach (Match ngClassDirectiveMatch in ngClassDirectiveMatches) { GroupCollection groups = ngClassDirectiveMatch.Groups; Group directiveNameGroup = groups["directiveName"]; int directiveNamePosition = directiveNameGroup.Index; string originalDirectiveName = directiveNameGroup.Value; string normalizedDirectiveName = NormalizeDirectiveName(originalDirectiveName); innerContext.IncreasePosition(directiveNamePosition - currentPosition); currentPosition = directiveNamePosition; if (directiveNameHandler != null) { directiveNameHandler(context, originalDirectiveName, normalizedDirectiveName); } Group expressionGroup = groups["expression"]; if (expressionGroup.Success) { int expressionPosition = expressionGroup.Index; string expression = expressionGroup.Value.Trim(); innerContext.IncreasePosition(expressionPosition - currentPosition); currentPosition = expressionPosition; if (expressionHandler != null) { expressionHandler(context, expression); } } Group semicolonGroup = groups["semicolon"]; if (semicolonGroup.Success) { int semicolonPosition = semicolonGroup.Index; innerContext.IncreasePosition(semicolonPosition - currentPosition); currentPosition = semicolonPosition; if (semicolonHandler != null) { semicolonHandler(context); } } } } }
/// <summary> /// Constructs instance of markup parsing context /// </summary> /// <param name="innerContext">Inner markup parsing context</param> internal MarkupParsingContext(InnerMarkupParsingContext innerContext) { _innerContext = innerContext; }
/// <summary> /// Parses HTML content /// </summary> /// <param name="content">HTML content</param> public void Parse(string content) { int contentLength = content.Length; if (contentLength == 0) { return; } lock (_parsingSynchronizer) { _innerContext = new InnerMarkupParsingContext(content); _context = new MarkupParsingContext(_innerContext); int endPosition = contentLength - 1; int previousPosition = -1; try { while (_innerContext.Position <= endPosition) { bool isProcessed = false; HtmlTag lastStackedTag = _tagStack.LastOrDefault(); // Make sure we're not in a tag, that contains embedded code if (lastStackedTag == null || !lastStackedTag.Flags.HasFlag(HtmlTagFlags.EmbeddedCode)) { int firstCharPosition = _innerContext.Position; char firstCharValue; bool firstCharExist = content.TryGetChar(firstCharPosition, out firstCharValue); if (firstCharExist && firstCharValue == '<') { int secondCharPosition = firstCharPosition + 1; char secondCharValue; bool secondCharExist = content.TryGetChar(secondCharPosition, out secondCharValue); if (secondCharExist) { if (secondCharValue.IsAlphaNumeric()) { // Start tag isProcessed = ProcessStartTag(); } else { int thirdCharPosition = secondCharPosition + 1; char thirdCharValue; bool thirdCharExist = content.TryGetChar(thirdCharPosition, out thirdCharValue); if (thirdCharExist) { switch (secondCharValue) { case '/': if (thirdCharValue.IsAlphaNumeric()) { isProcessed = ProcessEndTag(); } break; case '!': switch (thirdCharValue) { case '-': int fourthCharPosition = thirdCharPosition + 1; char fourthCharValue; bool fourthCharExist = content.TryGetChar(fourthCharPosition, out fourthCharValue); if (fourthCharExist && fourthCharValue == '-') { // Comments int fifthCharPosition = fourthCharPosition + 1; char fifthCharValue; bool fifthCharExist = content.TryGetChar(fifthCharPosition, out fifthCharValue); if (fifthCharExist) { if (fifthCharValue == '[') { // Revealed validating If conditional comments // (e.g. <!--[if ... ]><!--> or <!--[if ... ]>-->) isProcessed = ProcessRevealedValidatingIfComment(); if (!isProcessed) { // Hidden If conditional comments (e.g. <!--[if ... ]>) isProcessed = ProcessHiddenIfComment(); } } else { // Revealed validating End If conditional comments // (e.g. <!--<![endif]-->) isProcessed = ProcessRevealedValidatingEndIfComment(); } } if (!isProcessed) { // HTML comments isProcessed = ProcessComment(); } } break; case '[': // Remaining conditional comments // Hidden End If conditional comment (e.g. <![endif]-->) isProcessed = ProcessHiddenEndIfComment(); if (!isProcessed) { // Revealed If conditional comment (e.g. <![if ... ]>) isProcessed = ProcessRevealedIfComment(); } if (!isProcessed) { // Revealed End If conditional comment (e.g. <![endif]>) isProcessed = ProcessRevealedEndIfComment(); } break; case 'D': case 'd': // Doctype declaration isProcessed = ProcessDoctype(); break; } break; case '?': // XML declaration isProcessed = ProcessXmlDeclaration(); break; } } } } } if (!isProcessed) { // Text ProcessText(); } } else { // Embedded code ProcessEmbeddedCode(); } if (_innerContext.Position == previousPosition) { throw new MarkupParsingException( string.Format(Strings.ErrorMessage_MarkupParsingFailed, "HTML"), _innerContext.NodeCoordinates, _innerContext.GetSourceFragment()); } previousPosition = _innerContext.Position; } // Clean up any remaining tags ParseEndTag(); // Check whether there were not closed conditional comment if (_conditionalCommentStack.Count > 0) { throw new MarkupParsingException( Strings.ErrorMessage_NotClosedConditionalComment, _innerContext.NodeCoordinates, _innerContext.GetSourceFragment()); } } catch (MarkupParsingException) { throw; } finally { _tagStack.Clear(); _htmlTagFlagsCache.Clear(); _customHtmlTagFlagsCache.Clear(); _conditionalCommentStack.Clear(); _conditionalCommentOpened = false; _xmlTagStack.Clear(); _context = null; _innerContext = null; } } }
/// <summary> /// Parses XML content /// </summary> /// <param name="content">XML content</param> public void Parse(string content) { int contentLength = content.Length; if (contentLength == 0) { return; } lock (_parsingSynchronizer) { _innerContext = new InnerMarkupParsingContext(content); _context = new MarkupParsingContext(_innerContext); _tagStack = new Stack<StackedXmlTag>(); int endPosition = contentLength - 1; int previousPosition = -1; try { while (_innerContext.Position <= endPosition) { bool isProcessed = false; if (content.CustomStartsWith("<", _innerContext.Position, StringComparison.Ordinal)) { if (content.CustomStartsWith("</", _innerContext.Position, StringComparison.Ordinal)) { // End tag isProcessed = ProcessEndTag(); } else if (content.CustomStartsWith("<!", _innerContext.Position, StringComparison.Ordinal)) { // XML comments isProcessed = ProcessComment(); if (!isProcessed) { // CDATA sections isProcessed = ProcessCdataSection(); } if (!isProcessed) { // Doctype declaration isProcessed = ProcessDoctype(); } } else if (content.CustomStartsWith("<?", _innerContext.Position, StringComparison.Ordinal)) { // XML declaration and processing instructions isProcessed = ProcessProcessingInstruction(); } else { // Start tag isProcessed = ProcessStartTag(); } } if (!isProcessed) { // Text ProcessText(); } if (_innerContext.Position == previousPosition) { throw new XmlParsingException( string.Format(Strings.ErrorMessage_MarkupParsingFailed, "XML"), _innerContext.NodeCoordinates, _innerContext.GetSourceFragment()); } previousPosition = _innerContext.Position; } // Check whether there were not closed tags if (_tagStack.Count > 0) { StackedXmlTag stackedTag = _tagStack.Pop(); throw new XmlParsingException( string.Format(Strings.ErrorMessage_NotClosedTag, stackedTag.Name), stackedTag.Coordinates, SourceCodeNavigator.GetSourceFragment(_innerContext.SourceCode, stackedTag.Coordinates)); } } catch (XmlParsingException) { throw; } finally { _tagStack.Clear(); _context = null; _innerContext = null; } } }
/// <summary> /// Parses a Mustache-style markup /// </summary> /// <param name="content">Mustache-style markup</param> /// <param name="mustacheStyleTagHandler">Mustache-style tags handler</param> /// <param name="textHandler">Text handler</param> public static void ParseMarkup(string content, MustacheStyleTagDelegate mustacheStyleTagHandler, TextDelegate textHandler) { var innerContext = new InnerMarkupParsingContext(content); var context = new MarkupParsingContext(innerContext); MatchCollection mustacheStyleTagMatches = _mustacheStyleTagRegex.Matches(content); if (mustacheStyleTagMatches.Count == 0) { if (textHandler != null) { textHandler(context, content); } innerContext.IncreasePosition(content.Length); return; } int currentPosition = 0; int endPosition = content.Length - 1; foreach (Match mustacheStyleTagMatch in mustacheStyleTagMatches) { int mustacheStyleTagPosition = mustacheStyleTagMatch.Index; int mustacheStyleTagLength = mustacheStyleTagMatch.Length; if (mustacheStyleTagPosition > currentPosition) { string text = content.Substring(currentPosition, mustacheStyleTagPosition - currentPosition); if (textHandler != null) { textHandler(context, text); } innerContext.IncreasePosition(text.Length); } GroupCollection mustacheStyleTagGroups = mustacheStyleTagMatch.Groups; string expression = mustacheStyleTagGroups["expression"].Value; string startDelimiter = mustacheStyleTagGroups["startDelimiter"].Value; string endDelimiter = mustacheStyleTagGroups["endDelimiter"].Value; if (expression.StartsWith("{") && expression.EndsWith("}")) { expression = expression.Substring(1, expression.Length - 2); startDelimiter = "{{{"; endDelimiter = "}}}"; } if (mustacheStyleTagHandler != null) { mustacheStyleTagHandler(context, expression, startDelimiter, endDelimiter); } innerContext.IncreasePosition(mustacheStyleTagLength); currentPosition = mustacheStyleTagPosition + mustacheStyleTagLength; } if (currentPosition > 0 && currentPosition <= endPosition) { string text = content.Substring(currentPosition, endPosition - currentPosition + 1); if (textHandler != null) { textHandler(context, text); } innerContext.IncreasePosition(text.Length); } }
/// <summary> /// Parses XML content /// </summary> /// <param name="content">XML content</param> public void Parse(string content) { int contentLength = content.Length; if (contentLength == 0) { return; } lock (_parsingSynchronizer) { _innerContext = new InnerMarkupParsingContext(content); _context = new MarkupParsingContext(_innerContext); _tagStack = new Stack <StackedXmlTag>(); int endPosition = contentLength - 1; int previousPosition = -1; try { while (_innerContext.Position <= endPosition) { bool isProcessed = false; if (content.CustomStartsWith("<", _innerContext.Position, StringComparison.Ordinal)) { if (content.CustomStartsWith("</", _innerContext.Position, StringComparison.Ordinal)) { // End tag isProcessed = ProcessEndTag(); } else if (content.CustomStartsWith("<!", _innerContext.Position, StringComparison.Ordinal)) { // XML comments isProcessed = ProcessComment(); if (!isProcessed) { // CDATA sections isProcessed = ProcessCdataSection(); } if (!isProcessed) { // Doctype declaration isProcessed = ProcessDoctype(); } } else if (content.CustomStartsWith("<?", _innerContext.Position, StringComparison.Ordinal)) { // XML declaration and processing instructions isProcessed = ProcessProcessingInstruction(); } else { // Start tag isProcessed = ProcessStartTag(); } } if (!isProcessed) { // Text ProcessText(); } if (_innerContext.Position == previousPosition) { throw new XmlParsingException( string.Format(Strings.ErrorMessage_MarkupParsingFailed, "XML"), _innerContext.NodeCoordinates, _innerContext.GetSourceFragment()); } previousPosition = _innerContext.Position; } // Check whether there were not closed tags if (_tagStack.Count > 0) { StackedXmlTag stackedTag = _tagStack.Pop(); throw new XmlParsingException( string.Format(Strings.ErrorMessage_NotClosedTag, stackedTag.Name), stackedTag.Coordinates, SourceCodeNavigator.GetSourceFragment(_innerContext.SourceCode, stackedTag.Coordinates)); } } catch (XmlParsingException) { throw; } finally { _tagStack.Clear(); _context = null; _innerContext = null; } } }
/// <summary> /// Parses HTML content /// </summary> /// <param name="content">HTML content</param> public void Parse(string content) { int contentLength = content.Length; if (contentLength == 0) { return; } lock (_parsingSynchronizer) { _innerContext = new InnerMarkupParsingContext(content); _context = new MarkupParsingContext(_innerContext); int endPosition = contentLength - 1; int previousPosition = -1; try { while (_innerContext.Position <= endPosition) { bool isProcessed = false; HtmlTag lastStackedTag = _tagStack.LastOrDefault(); // Make sure we're not in a tag, that contains embedded code if (lastStackedTag == null || !lastStackedTag.Flags.IsSet(HtmlTagFlags.EmbeddedCode)) { if (_innerContext.PeekCurrentChar() == '<') { switch (_innerContext.PeekNextChar()) { case char c when c.IsAlphaNumeric(): // Start tag isProcessed = ProcessStartTag(); break; case '/': if (_innerContext.PeekNextChar().IsAlphaNumeric()) { // End tag isProcessed = ProcessEndTag(); } break; case '!': switch (_innerContext.PeekNextChar()) { case '-': if (_innerContext.PeekNextChar() == '-') { // Comments if (_innerContext.PeekNextChar() == '[') { // Revealed validating If conditional comments // (e.g. <!--[if ... ]><!--> or <!--[if ... ]>-->) isProcessed = ProcessRevealedValidatingIfComment(); if (!isProcessed) { // Hidden If conditional comments (e.g. <!--[if ... ]>) isProcessed = ProcessHiddenIfComment(); } } else { // Revealed validating End If conditional comments // (e.g. <!--<![endif]-->) isProcessed = ProcessRevealedValidatingEndIfComment(); } if (!isProcessed) { // HTML comments isProcessed = ProcessComment(); } } break; case '[': switch (_innerContext.PeekNextChar()) { case 'i': case 'I': // Revealed If conditional comment (e.g. <![if ... ]>) isProcessed = ProcessRevealedIfComment(); break; case 'e': case 'E': // Hidden End If conditional comment (e.g. <![endif]-->) isProcessed = ProcessHiddenEndIfComment(); if (!isProcessed) { // Revealed End If conditional comment (e.g. <![endif]>) isProcessed = ProcessRevealedEndIfComment(); } break; case 'C': // CDATA sections isProcessed = ProcessCdataSection(); break; } break; case 'D': case 'd': // Doctype declaration isProcessed = ProcessDoctype(); break; } break; case '?': // XML declaration isProcessed = ProcessXmlDeclaration(); break; } } if (!isProcessed) { // Text ProcessText(); } } else { // Embedded code ProcessEmbeddedCode(); } if (_innerContext.Position == previousPosition) { throw new MarkupParsingException( string.Format(Strings.ErrorMessage_MarkupParsingFailed, "HTML"), _innerContext.NodeCoordinates, _innerContext.GetSourceFragment()); } previousPosition = _innerContext.Position; } // Clean up any remaining tags ParseEndTag(); // Check whether there were not closed conditional comment if (_conditionalCommentStack.Count > 0) { throw new MarkupParsingException( Strings.ErrorMessage_NotClosedConditionalComment, _innerContext.NodeCoordinates, _innerContext.GetSourceFragment()); } } catch (MarkupParsingException) { throw; } finally { _tagStack.Clear(); _tempAttributes.Clear(); _conditionalCommentStack.Clear(); _conditionalCommentOpened = false; _xmlTagStack.Clear(); _context = null; _innerContext = null; } } }