335 lines
12 KiB
C#
335 lines
12 KiB
C#
|
//------------------------------------------------------------------------------
|
||
|
// <copyright file="SimpleParser.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
//------------------------------------------------------------------------------
|
||
|
|
||
|
using System.Collections;
|
||
|
using System.Globalization;
|
||
|
using System.Text;
|
||
|
using System.Text.RegularExpressions;
|
||
|
using System.Web;
|
||
|
using System.Web.RegularExpressions;
|
||
|
using System.Web.Util;
|
||
|
using System.Web.UI.MobileControls;
|
||
|
|
||
|
namespace System.Web.UI.Design.MobileControls.Util
|
||
|
{
|
||
|
[
|
||
|
System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand,
|
||
|
Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)
|
||
|
]
|
||
|
[Obsolete("The System.Web.Mobile.dll assembly has been deprecated and should no longer be used. For information about how to develop ASP.NET mobile applications, see http://go.microsoft.com/fwlink/?LinkId=157231.")]
|
||
|
internal class SimpleParser : BaseParser
|
||
|
{
|
||
|
private const int _stackInitialSize = 100;
|
||
|
private static Regex _unclosedTagRegex = null;
|
||
|
|
||
|
private const RegexOptions _options =
|
||
|
RegexOptions.Singleline | RegexOptions.Multiline;
|
||
|
|
||
|
private const String _pattern =
|
||
|
@"\G<(?<tagname>[\w:\.]+)" +
|
||
|
@"(" +
|
||
|
@"\s+(?<attrname>\w[-\w:]*)(" + // Attribute name
|
||
|
@"\s*=\s*""(?<attrval>[^""]*)""|" + // ="bar" attribute value
|
||
|
@"\s*=\s*'(?<attrval>[^']*)'|" + // ='bar' attribute value
|
||
|
@"\s*=\s*(?<attrval><%#.*?%>)|" + // =<%#expr%> attribute value
|
||
|
@"\s*=\s*(?!'|"")(?<attrval>[^\s=/>]*)(?!'|"")|" + // =bar attribute value
|
||
|
@"(?<attrval>\s*?)" + // no attrib value (with no '=')
|
||
|
@")" +
|
||
|
@")*" +
|
||
|
@"\s*(?<empty>)?>";
|
||
|
//@"\s*(?<empty>/)?>";
|
||
|
|
||
|
private static ElementTable _endTagOptionalElement = null;
|
||
|
|
||
|
private readonly static Regex _tagRegex = new TagRegex();
|
||
|
private readonly static Regex _directiveRegex = new DirectiveRegex();
|
||
|
private readonly static Regex _endtagRegex = new EndTagRegex();
|
||
|
private readonly static Regex _aspCodeRegex = new AspCodeRegex();
|
||
|
private readonly static Regex _aspExprRegex = new AspExprRegex();
|
||
|
private readonly static Regex _databindExprRegex = new DatabindExprRegex();
|
||
|
private readonly static Regex _commentRegex = new CommentRegex();
|
||
|
private readonly static Regex _includeRegex = new IncludeRegex();
|
||
|
private readonly static Regex _textRegex = new TextRegex();
|
||
|
|
||
|
// Regexes used in DetectSpecialServerTagError
|
||
|
private readonly static Regex _gtRegex = new GTRegex();
|
||
|
private readonly static Regex _ltRegex = new LTRegex();
|
||
|
private readonly static Regex _serverTagsRegex = new ServerTagsRegex();
|
||
|
private readonly static Regex _runatServerRegex = new RunatServerRegex();
|
||
|
|
||
|
/* Regex patterns
|
||
|
AspCodeRegex : \G<%(?!@)(?<code>.*?)%>
|
||
|
AspExprRegex : \G<%\s*?=(?<code>.*?)?%>
|
||
|
CommentRegex : \G<%--(([^-]*)-)*?-%>
|
||
|
DataBindExprRegex : \G<%#(?<code>.*?)?%>
|
||
|
DirectiveRegex : \G<%\s*@(\s*(?<attrname>\w+(?=\W))(\s*(?<equal>=)\s*"(?<attrval>[^"]*)"|\s*(?<equal>=)\s*'(?<attrval>[^']*)'|\s*(?<equal>=)\s*(?<attrval>[^\s%>]*)|(?<equal>)(?<attrval>\s*?)))*\s*?%>
|
||
|
EndTagRegex : \G</(?<tagname>[\w:\.]+)\s*>
|
||
|
GTRegex : [^%]>
|
||
|
IncludeRegex : \G<!--\s*#(?i:include)\s*(?<pathtype>[\w]+)\s*=\s*["']?(?<filename>[^\"']*?)["']?\s*-->
|
||
|
LTRegex : <
|
||
|
RunATServerRegex : runat\W*server
|
||
|
ServerTagsRegex : <%(?!#)(([^%]*)%)*?>
|
||
|
TagRegex : \G<(?<tagname>[\w:\.]+)(\s+(?<attrname>[-\w]+)(\s*=\s*"(?<attrval>[^"]*)"|\s*=\s*'(?<attrval>[^']*)'|\s*=\s*(?<attrval><%#.*?%>)|\s*=\s*(?<attrval>[^\s=/>]*)|(?<attrval>\s*?)))*\s*(?<empty>/)?>
|
||
|
TextRegex : \G[^<]+
|
||
|
|
||
|
//SimpleDirectiveRegex simpleDirectiveRegex = new SimpleDirectiveRegex();
|
||
|
*/
|
||
|
// static helper type should not be instantiated.
|
||
|
private SimpleParser() {
|
||
|
}
|
||
|
|
||
|
static SimpleParser()
|
||
|
{
|
||
|
_unclosedTagRegex = new Regex(_pattern, _options);
|
||
|
_endTagOptionalElement = new ElementTable();
|
||
|
|
||
|
/* following defnitions from MSDN Online WorkShop
|
||
|
http://msdn.microsoft.com/workshop/c-frame.htm#/workshop/author/default.asp
|
||
|
*/
|
||
|
_endTagOptionalElement.AddRange(
|
||
|
new String[] {
|
||
|
"area", "base", "basefront", "bgsound", "br",
|
||
|
"col", "colgroup", "dd", "dt", "embed", "frame",
|
||
|
"hr", "img", "input", "isindex", "li", "link",
|
||
|
"meta", "option", "p", "param", "rt"
|
||
|
});
|
||
|
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Simple parsing to check if input fragment is well-formed,
|
||
|
/// HTML elements that do not required end tags (i.e. <BR>)
|
||
|
/// will be ignored by this parser.
|
||
|
/// </summary>
|
||
|
/// <param name="text">
|
||
|
/// text being parsed
|
||
|
/// </param>
|
||
|
internal static bool IsWellFormed(String text)
|
||
|
{
|
||
|
int textPos = 0;
|
||
|
TagStack stack = new TagStack();
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
Match match = null;
|
||
|
|
||
|
// 1: scan for text up to the next tag.
|
||
|
if ((match = _textRegex.Match(text, textPos)).Success)
|
||
|
{
|
||
|
textPos = match.Index + match.Length;
|
||
|
}
|
||
|
|
||
|
// we might be done now
|
||
|
if (textPos == text.Length)
|
||
|
{
|
||
|
while (!stack.IsEmpty())
|
||
|
{
|
||
|
if (!IsEndTagOptional(stack.Pop()))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// First check if it's a unclosed tag (i.e. <mobile:Form >)
|
||
|
if ((match = _unclosedTagRegex.Match(text, textPos)).Success)
|
||
|
{
|
||
|
String startTag = match.Groups["tagname"].Value;
|
||
|
stack.Push(startTag);
|
||
|
}
|
||
|
|
||
|
// Check to see if it's a tag
|
||
|
else if ((match = _tagRegex.Match(text, textPos)).Success)
|
||
|
{
|
||
|
// skip
|
||
|
}
|
||
|
|
||
|
// Check to see if it's an end tag
|
||
|
else if ((match = _endtagRegex.Match(text, textPos)).Success)
|
||
|
{
|
||
|
String endTag = match.Groups["tagname"].Value;
|
||
|
bool matched = false;
|
||
|
|
||
|
while (!stack.IsEmpty())
|
||
|
{
|
||
|
String startTag = stack.Pop();
|
||
|
|
||
|
if (String.Compare(endTag, startTag, StringComparison.OrdinalIgnoreCase) != 0)
|
||
|
{
|
||
|
if (IsEndTagOptional(startTag))
|
||
|
{
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// no match against start tag that requires an end tag
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
// we found a match here.
|
||
|
matched = true;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (!matched && stack.IsEmpty())
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Check to see if it's a directive (i.e. <%@ %> block)
|
||
|
else if ((match = _directiveRegex.Match(text, textPos)).Success)
|
||
|
{
|
||
|
// skip
|
||
|
}
|
||
|
|
||
|
// Check to see if it's a server side include
|
||
|
// e.g. <!-- #include file="foo.inc" -->
|
||
|
else if ((match = _includeRegex.Match(text, textPos)).Success)
|
||
|
{
|
||
|
// skip it
|
||
|
}
|
||
|
|
||
|
// Check to see if it's a comment (<%-- --%> block
|
||
|
// e.g. <!-- Blah! -->
|
||
|
else if ((match = _commentRegex.Match(text, textPos)).Success)
|
||
|
{
|
||
|
// skip
|
||
|
}
|
||
|
|
||
|
// Check to see if it's an asp expression block (i.e. <%= %> block)
|
||
|
else if ((match = _aspExprRegex.Match(text, textPos)).Success)
|
||
|
{
|
||
|
// skip
|
||
|
}
|
||
|
|
||
|
// Check to see if it's a databinding expression block (i.e. <%# %> block)
|
||
|
// This does not include <%# %> blocks used as values for
|
||
|
// attributes of server tags.
|
||
|
else if ((match = _databindExprRegex.Match(text, textPos)).Success)
|
||
|
{
|
||
|
// skip
|
||
|
}
|
||
|
|
||
|
// Check to see if it's an asp code block
|
||
|
else if ((match = _aspCodeRegex.Match(text, textPos)).Success)
|
||
|
{
|
||
|
// skip
|
||
|
}
|
||
|
|
||
|
// Did we process the block that started with a '<'?
|
||
|
if (match == null || !match.Success)
|
||
|
{
|
||
|
// Skip the '<'
|
||
|
textPos++;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
textPos = match.Index + match.Length;
|
||
|
}
|
||
|
|
||
|
// we might be done now
|
||
|
if (textPos == text.Length)
|
||
|
{
|
||
|
while (!stack.IsEmpty())
|
||
|
{
|
||
|
if (!IsEndTagOptional(stack.Pop()))
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static bool IsEndTagOptional(String element)
|
||
|
{
|
||
|
return (_endTagOptionalElement.Contains(element));
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Private class used to store lowercase tags in a stack
|
||
|
/// return String.Empty if stack is empty
|
||
|
/// </summary>
|
||
|
[
|
||
|
System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand,
|
||
|
Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)
|
||
|
]
|
||
|
private class TagStack
|
||
|
{
|
||
|
private Stack _tagStack = null;
|
||
|
|
||
|
internal TagStack() : this(_stackInitialSize)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
internal TagStack(int initialCapacity)
|
||
|
{
|
||
|
_tagStack = new Stack(initialCapacity);
|
||
|
}
|
||
|
|
||
|
internal void Push(String tagName)
|
||
|
{
|
||
|
_tagStack.Push(tagName.ToLower(CultureInfo.InvariantCulture));
|
||
|
}
|
||
|
|
||
|
internal String Pop()
|
||
|
{
|
||
|
if (IsEmpty())
|
||
|
{
|
||
|
return String.Empty;
|
||
|
}
|
||
|
return (String)_tagStack.Pop();
|
||
|
}
|
||
|
|
||
|
internal bool IsEmpty()
|
||
|
{
|
||
|
return (_tagStack.Count == 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Private class used to store recognizable lowercase elements
|
||
|
/// return true if element is in the list, otherwise false
|
||
|
/// </summary>
|
||
|
[
|
||
|
System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand,
|
||
|
Flags=System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode)
|
||
|
]
|
||
|
private class ElementTable
|
||
|
{
|
||
|
private Hashtable _table = null;
|
||
|
|
||
|
internal ElementTable() : this(_stackInitialSize)
|
||
|
{}
|
||
|
|
||
|
internal ElementTable(int initialCapacity)
|
||
|
{
|
||
|
_table = new Hashtable(initialCapacity, StringComparer.OrdinalIgnoreCase);
|
||
|
}
|
||
|
|
||
|
internal void Add(String key)
|
||
|
{
|
||
|
_table.Add(key, true);
|
||
|
}
|
||
|
|
||
|
internal bool Contains(String key)
|
||
|
{
|
||
|
return (_table.Contains(key));
|
||
|
}
|
||
|
|
||
|
internal void AddRange(String[] keysCollection)
|
||
|
{
|
||
|
foreach (String key in keysCollection)
|
||
|
{
|
||
|
this.Add(key);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|