//
// Authors:
// Gonzalo Paniagua Javier (gonzalo@ximian.com)
+// Marek Habersack <mhabersack@novell.com>
//
// (C) 2002,2003 Ximian, Inc (http://www.ximian.com)
-// Copyright (c) 2004,2006 Novell, Inc (http://www.novell.com)
+// Copyright (c) 2004-2009 Novell, Inc (http://www.novell.com)
//
//
using System.Web.UI.HtmlControls;
using System.Web.Util;
+#if NET_2_0
+using System.Collections.Generic;
+#endif
+
namespace System.Web.Compilation
{
class BuilderLocation
}
}
+ enum TextBlockType
+ {
+ Verbatim,
+ Expression,
+ Tag,
+ Comment
+ }
+
+ sealed class TextBlock
+ {
+ public string Content;
+ public readonly TextBlockType Type;
+
+ public TextBlock (TextBlockType type, string content)
+ {
+ Content = content;
+ Type = type;
+ }
+ }
+
class AspGenerator
{
+#if NET_2_0
+ const int READ_BUFFER_SIZE = 8192;
+
+ internal static Regex DirectiveRegex = new Regex (@"<%\s*@(\s*(?<attrname>\w[\w:]*(?=\W))(\s*(?<equal>=)\s*""(?<attrval>[^""]*)""|\s*(?<equal>=)\s*'(?<attrval>[^']*)'|\s*(?<equal>=)\s*(?<attrval>[^\s%>]*)|(?<equal>)(?<attrval>\s*?)))*\s*?%>", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+#endif
+ static readonly Regex runatServer = new Regex (@"<[\w:\.]+.*?runat=[""']?server[""']?.*?/?>",
+ RegexOptions.Compiled | RegexOptions.Singleline |
+ RegexOptions.Multiline | RegexOptions.IgnoreCase |
+ RegexOptions.CultureInvariant);
+
+ static readonly Regex endOfTag = new Regex (@"</[\w:\.]+\s*>",
+ RegexOptions.Compiled | RegexOptions.Singleline |
+ RegexOptions.Multiline | RegexOptions.IgnoreCase |
+ RegexOptions.CultureInvariant);
+
+ static readonly Regex expressionRegex = new Regex (@"<%.*%>",
+ RegexOptions.Compiled | RegexOptions.Singleline |
+ RegexOptions.Multiline | RegexOptions.IgnoreCase |
+ RegexOptions.CultureInvariant);
+
+ static readonly Regex clientCommentRegex = new Regex (@"<!--.*-->",
+ RegexOptions.Compiled | RegexOptions.Singleline |
+ RegexOptions.Multiline | RegexOptions.IgnoreCase |
+ RegexOptions.CultureInvariant);
+
ParserStack pstack;
BuilderLocationStack stack;
TemplateParser tparser;
bool inForm;
bool useOtherTags;
TagType lastTag;
+#if NET_2_0
+ AspComponentFoundry componentFoundry;
+ Stream inputStream;
+ public AspGenerator (TemplateParser tparser, AspComponentFoundry componentFoundry) : this (tparser)
+ {
+ this.componentFoundry = componentFoundry;
+ }
+#endif
+
public AspGenerator (TemplateParser tparser)
{
this.tparser = tparser;
text = new StringBuilder ();
stack = new BuilderLocationStack ();
+
+#if !NET_2_0
rootBuilder = new RootBuilder (tparser);
- stack.Push (rootBuilder, null);
tparser.RootBuilder = rootBuilder;
+ stack.Push (rootBuilder, null);
+#endif
pstack = new ParserStack ();
}
public RootBuilder RootBuilder {
- get { return tparser.RootBuilder; }
+ get { return rootBuilder; }
}
public AspParser Parser {
return tparser.PageParserFilter;
}
}
+
+ // KLUDGE WARNING
+ //
+ // The kludge to determine the base type of the to-be-generated ASP.NET class is
+ // very unfortunate but with our current parser it is, unfortunately, necessary. The
+ // reason for reading the entire file into memory and parsing it with a regexp is
+ // that we need to read the main directive (i.e. <%@Page %>, <%@Control %> etc),
+ // pass it to the page parser filter if it exists, and finally read the inherits
+ // attribute of the directive to get access to the base type of the class to be
+ // generated. On that type we check whether it is decorated with the
+ // FileLevelControlBuilder attribute and, if yes, use the indicated type as the
+ // RootBuilder. This is necessary for the ASP.NET MVC views using the "generic"
+ // inherits declaration to work properly. Our current parser is not able to parse
+ // the input file out of sequence (i.e. directives first, then the rest) so we need
+ // to do what we do below, alas.
+ Hashtable GetDirectiveAttributesDictionary (string skipKeyName, CaptureCollection names, CaptureCollection values)
+ {
+ var ret = new Hashtable (StringComparer.InvariantCultureIgnoreCase);
+
+ int index = 0;
+ string keyName;
+ foreach (Capture c in names) {
+ keyName = c.Value;
+ if (String.Compare (skipKeyName, keyName, StringComparison.OrdinalIgnoreCase) == 0) {
+ index++;
+ continue;
+ }
+
+ ret.Add (c.Value, values [index++].Value);
+ }
+
+ return ret;
+ }
+
+ string GetDirectiveName (CaptureCollection names)
+ {
+ string val;
+ foreach (Capture c in names) {
+ val = c.Value;
+ if (Directive.IsDirective (val))
+ return val;
+ }
+
+ return tparser.DefaultDirectiveName;
+ }
+
+ int GetLineNumberForIndex (string fileContents, int index)
+ {
+ int line = 1;
+ char c;
+ bool foundCR = false;
+
+ for (int pos = 0; pos < index; pos++) {
+ c = fileContents [pos];
+ if (c == '\n' || foundCR) {
+ line++;
+ foundCR = false;
+ }
+
+ foundCR = (c == '\r');
+ }
+
+ return line;
+ }
+
+ int GetNumberOfLinesForRange (string fileContents, int index, int length)
+ {
+ int lines = 0;
+ int stop = index + length;
+ char c;
+ bool foundCR = false;
+
+ for (int pos = index; pos < stop; pos++) {
+ c = fileContents [pos];
+ if (c == '\n' || foundCR) {
+ lines++;
+ foundCR = false;
+ }
+
+ foundCR = (c == '\r');
+ }
+
+ return lines;
+ }
+
+ Type GetInheritedType (string fileContents, string filename)
+ {
+ MatchCollection matches = DirectiveRegex.Matches (fileContents);
+ if (matches == null || matches.Count == 0)
+ return null;
+
+ string wantedDirectiveName = tparser.DefaultDirectiveName.ToLower ();
+ string directiveName;
+ GroupCollection groups;
+ CaptureCollection ccNames;
+
+ foreach (Match match in matches) {
+ groups = match.Groups;
+ if (groups.Count < 6)
+ continue;
+
+ ccNames = groups [3].Captures;
+ directiveName = GetDirectiveName (ccNames);
+ if (String.IsNullOrEmpty (directiveName))
+ continue;
+
+ if (String.Compare (directiveName.ToLower (), wantedDirectiveName, StringComparison.Ordinal) != 0)
+ continue;
+
+ var loc = new Location (null);
+ int index = match.Index;
+
+ loc.Filename = filename;
+ loc.BeginLine = GetLineNumberForIndex (fileContents, index);
+ loc.EndLine = loc.BeginLine + GetNumberOfLinesForRange (fileContents, index, match.Length);
+
+ tparser.Location = loc;
+ tparser.allowedMainDirectives = 2;
+ tparser.AddDirective (wantedDirectiveName, GetDirectiveAttributesDictionary (wantedDirectiveName, ccNames, groups [5].Captures));
+
+ return tparser.BaseType;
+ }
+
+ return null;
+ }
+
+ string ReadFileContents (Stream inputStream, string filename)
+ {
+ string ret = null;
+
+ if (inputStream != null) {
+ if (inputStream.CanSeek) {
+ long curPos = inputStream.Position;
+ inputStream.Seek (0, SeekOrigin.Begin);
+
+ Encoding enc = WebEncoding.FileEncoding;
+ StringBuilder sb = new StringBuilder ();
+ byte[] buffer = new byte [READ_BUFFER_SIZE];
+ int nbytes;
+
+ while ((nbytes = inputStream.Read (buffer, 0, READ_BUFFER_SIZE)) > 0)
+ sb.Append (enc.GetString (buffer, 0, nbytes));
+ inputStream.Seek (curPos, SeekOrigin.Begin);
+
+ ret = sb.ToString ();
+ sb.Length = 0;
+ sb.Capacity = 0;
+ } else {
+ FileStream fs = inputStream as FileStream;
+ if (fs != null) {
+ string fname = fs.Name;
+ try {
+ if (File.Exists (fname))
+ ret = File.ReadAllText (fname);
+ } catch {
+ // ignore
+ }
+ }
+ }
+ }
+
+ if (ret == null && !String.IsNullOrEmpty (filename) && String.Compare (filename, "@@inner_string@@", StringComparison.Ordinal) != 0) {
+ try {
+ if (File.Exists (filename))
+ ret = File.ReadAllText (filename);
+ } catch {
+ // ignore
+ }
+ }
+
+ return ret;
+ }
+
+ Type GetRootBuilderType (Stream inputStream, string filename)
+ {
+ Type ret = null;
+ string fileContents;
+
+ if (tparser != null)
+ fileContents = ReadFileContents (inputStream, filename);
+ else
+ fileContents = null;
+
+ if (!String.IsNullOrEmpty (fileContents)) {
+ Type inheritedType = GetInheritedType (fileContents, filename);
+ fileContents = null;
+ if (inheritedType != null) {
+ FileLevelControlBuilderAttribute attr;
+
+ try {
+ object[] attrs = inheritedType.GetCustomAttributes (typeof (FileLevelControlBuilderAttribute), true);
+ if (attrs != null && attrs.Length > 0)
+ attr = attrs [0] as FileLevelControlBuilderAttribute;
+ else
+ attr = null;
+ } catch {
+ attr = null;
+ }
+
+ ret = attr != null ? attr.BuilderType : null;
+ }
+ }
+
+ if (ret == null) {
+ if (tparser is PageParser)
+ return typeof (FileLevelPageControlBuilder);
+ else if (tparser is UserControlParser)
+ return typeof (FileLevelUserControlBuilder);
+ else
+ return typeof (RootBuilder);
+ } else
+ return ret;
+ }
+
+ void CreateRootBuilder (Stream inputStream, string filename)
+ {
+ if (rootBuilder != null)
+ return;
+
+ Type rootBuilderType = GetRootBuilderType (inputStream, filename);
+ rootBuilder = Activator.CreateInstance (rootBuilderType) as RootBuilder;
+ if (rootBuilder == null)
+ throw new HttpException ("Cannot create an instance of file-level control builder.");
+ rootBuilder.Init (tparser, null, null, null, null, null);
+ if (componentFoundry != null)
+ rootBuilder.Foundry = componentFoundry;
+
+ stack.Push (rootBuilder, null);
+ tparser.RootBuilder = rootBuilder;
+ }
#endif
BaseCompiler GetCompilerFromType ()
#if NET_2_0
parser.ParsingComplete += new ParsingCompleteHandler (ParsingCompleted);
tparser.AspGenerator = this;
+ CreateRootBuilder (inputStream, filename);
#endif
if (!pstack.Push (parser))
throw new ParseException (Location, "Infinite recursion detected including file: " + filename);
tparser.AddDependency (arvp);
}
-#if NET_2_0
- tparser.MD5Checksum = parser.MD5Checksum;
-#endif
}
#if NET_2_0
if (text.Length > 0)
FlushText ();
+#if NET_2_0
+ tparser.MD5Checksum = pstack.Parser.MD5Checksum;
+#endif
pstack.Pop ();
#if DEBUG
- PrintTree (rootBuilder, 0);
+ PrintTree (RootBuilder, 0);
#endif
if (stack.Count > 1 && pstack.Count == 0)
throw new ParseException (stack.Builder.Location,
"Expecting </" + stack.Builder.TagName + "> " + stack.Builder);
+
} finally {
if (reader != null)
reader.Close ();
public void Parse (Stream stream, string filename, bool doInitParser)
{
+#if NET_2_0
+ inputStream = stream;
+#endif
Parse (new StreamReader (stream, WebEncoding.FileEncoding), filename, doInitParser);
}
// The kludge supports only self-closing tags inside attributes.
//
// KLUDGE WARNING!!
- static readonly Regex runatServer=new Regex (@"<[\w:\.]+.*?runat=[""']?server[""']?.*?/>",
- RegexOptions.Compiled | RegexOptions.Singleline |
- RegexOptions.Multiline | RegexOptions.IgnoreCase);
bool ProcessTagsInAttributes (ILocation location, string tagid, TagAttributes attributes, TagType type)
{
if (attributes == null || attributes.Count == 0)
sb.AppendFormat ("\t<{0}", tagid);
foreach (string key in attributes.Keys) {
value = attributes [key] as string;
- if (value == null || value.Length < 16) // optimization
+ if (value == null || value.Length < 16) { // optimization
+ sb.AppendFormat (" {0}=\"{1}\"", key, value);
continue;
+ }
match = runatServer.Match (attributes [key] as string);
if (!match.Success) {
group = match.Groups [0];
index = group.Index;
length = group.Length;
-
- if (index > 0)
- TextParsed (location, String.Format (" {0}=\"{1}", key, value.Substring (0, index)));
- FlushText ();
+
+ TextParsed (location, String.Format (" {0}=\"{1}", key, index > 0 ? value.Substring (0, index) : String.Empty));;
+ FlushText ();
ParseAttributeTag (group.Value);
if (index + length < value.Length)
TextParsed (location, value.Substring (index + length) + "\"");
+ else
+ TextParsed (location, "\"");
}
if (type == TagType.SelfClosing)
sb.Append ("/>");
if (pfilter == null)
return;
- pfilter.ParseComplete (rootBuilder);
+ pfilter.ParseComplete (RootBuilder);
}
#endif
+
+ void CheckIfIncludeFileIsSecure (string filePath)
+ {
+ if (filePath == null || filePath.Length == 0)
+ return;
+
+ // a bit slow, but fully portable
+ string newdir = null;
+ Exception exception = null;
+ try {
+ string origdir = Directory.GetCurrentDirectory ();
+ Directory.SetCurrentDirectory (Path.GetDirectoryName (filePath));
+ newdir = Directory.GetCurrentDirectory ();
+ Directory.SetCurrentDirectory (origdir);
+ if (newdir [newdir.Length - 1] != '/')
+ newdir += "/";
+ } catch (DirectoryNotFoundException) {
+ return; // will be converted into 404
+ } catch (FileNotFoundException) {
+ return; // as above
+ } catch (Exception ex) {
+ // better safe than sorry
+ exception = ex;
+ }
+
+ if (exception != null || !StrUtils.StartsWith (newdir, HttpRuntime.AppDomainAppPath))
+ throw new ParseException (Location, "Files above the application's root directory cannot be included.");
+ }
void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
{
+ bool tagIgnored;
+
this.location = new Location (location);
if (tparser != null)
tparser.Location = location;
if (0 == String.Compare (tagid, "script", true, CultureInfo.InvariantCulture)) {
bool in_script = (inScript || ignore_text);
- if (in_script || (tagtype != TagType.Close && attributes != null)) {
- if ((in_script || attributes.IsRunAtServer ()) && ProcessScript (tagtype, attributes))
+ if (in_script) {
+ if (ProcessScript (tagtype, attributes))
+ return;
+ } else
+ if (ProcessScript (tagtype, attributes))
return;
- }
}
lastTag = tagtype;
tparser.AddDirective (tagid, attributes.GetDictionary (null));
break;
case TagType.Tag:
- if (ProcessTag (location, tagid, attributes, tagtype)) {
- useOtherTags = true;
+ if (ProcessTag (location, tagid, attributes, tagtype, out tagIgnored)) {
+ if (!tagIgnored)
+ useOtherTags = true;
break;
}
break;
case TagType.SelfClosing:
int count = stack.Count;
- if (!ProcessTag (location, tagid, attributes, tagtype)) {
+ if (!ProcessTag (location, tagid, attributes, tagtype, out tagIgnored) && !tagIgnored) {
string plainText = location.PlainText;
if (!ProcessTagsInAttributes (location, tagid, attributes, TagType.SelfClosing))
TextParsed (location, plainText);
Parse (tparser.MapPath (file), true);
} else {
string includeFilePath = GetIncludeFilePath (tparser.ParserDir, file);
+ CheckIfIncludeFileIsSecure (includeFilePath);
tparser.PushIncludeDir (Path.GetDirectoryName (includeFilePath));
try {
Parse (includeFilePath, true);
return Path.GetFullPath (Path.Combine (basedir, filename));
}
+
+ delegate bool CheckBlockEnd (string text);
+
+ bool CheckTagEndNeeded (string text)
+ {
+ return !text.EndsWith ("/>");
+ }
+
+#if NET_2_0
+ List <TextBlock>
+#else
+ ArrayList
+#endif
+ FindRegexBlocks (Regex rxStart, Regex rxEnd, CheckBlockEnd checkEnd, IList blocks, TextBlockType typeForMatches, bool discardBlocks)
+ {
+#if NET_2_0
+ var ret = new List <TextBlock> ();
+#else
+ ArrayList ret = new ArrayList ();
+#endif
+
+ foreach (TextBlock block in blocks) {
+ if (block.Type != TextBlockType.Verbatim) {
+ ret.Add (block);
+ continue;
+ }
+
+ int lastIndex = 0, index;
+ MatchCollection matches = rxStart.Matches (block.Content);
+ bool foundMatches = matches.Count > 0;
+ foreach (Match match in matches) {
+ foundMatches = true;
+ index = match.Index;
+ if (lastIndex < index)
+ ret.Add (new TextBlock (TextBlockType.Verbatim, block.Content.Substring (lastIndex, index - lastIndex)));
+
+ string value = match.Value;
+ if (rxEnd != null && checkEnd (value)) {
+ int startFrom = index + value.Length;
+ Match m = rxEnd.Match (block.Content, startFrom);
+ if (m.Success)
+ value += block.Content.Substring (startFrom, m.Index - startFrom) + m.Value;
+ }
+
+ if (!discardBlocks)
+ ret.Add (new TextBlock (typeForMatches, value));
+ lastIndex = index + value.Length;
+ }
+
+ if (lastIndex > 0 && lastIndex < block.Content.Length)
+ ret.Add (new TextBlock (TextBlockType.Verbatim, block.Content.Substring (lastIndex)));
+
+ if (!foundMatches)
+ ret.Add (block);
+ }
+
+ return ret;
+ }
+ IList SplitTextIntoBlocks (string text)
+ {
+#if NET_2_0
+ var ret = new List <TextBlock> ();
+#else
+ ArrayList ret = new ArrayList ();
+#endif
+
+ ret.Add (new TextBlock (TextBlockType.Verbatim, text));
+ ret = FindRegexBlocks (runatServer, endOfTag, CheckTagEndNeeded, ret, TextBlockType.Tag, false);
+ ret = FindRegexBlocks (expressionRegex, null, null, ret, TextBlockType.Expression, false);
+
+ return ret;
+ }
+
void TextParsed (ILocation location, string text)
{
if (ignore_text)
return;
- if (text.IndexOf ("<%") != -1 && !inScript) {
- if (this.text.Length > 0)
- FlushText (true);
- CodeRenderParser r = new CodeRenderParser (text, stack.Builder);
- r.AddChildren ();
- return;
+ IList blocks = SplitTextIntoBlocks (text);
+ foreach (TextBlock block in blocks) {
+ switch (block.Type) {
+ case TextBlockType.Verbatim:
+ this.text.Append (block.Content);
+ break;
+
+ case TextBlockType.Expression:
+ if (this.text.Length > 0)
+ FlushText (true);
+ CodeRenderParser r = new CodeRenderParser (block.Content, stack.Builder);
+ r.AddChildren (this);
+ break;
+
+ case TextBlockType.Tag:
+ ParseAttributeTag (block.Content);
+ break;
+ }
}
-
- this.text.Append (text);
- //PrintLocation (location);
}
void FlushText ()
if (!typeof (System.Web.UI.WebControls.Content).IsAssignableFrom (cb.ControlType))
return true;
- if (BuilderHasOtherThan (typeof (System.Web.UI.WebControls.Content), rootBuilder))
+ if (BuilderHasOtherThan (typeof (System.Web.UI.WebControls.Content), RootBuilder))
return false;
return true;
parent.AppendSubBuilder (builder);
}
- bool ProcessTag (ILocation location, string tagid, TagAttributes atts, TagType tagtype)
+ bool ProcessTag (ILocation location, string tagid, TagAttributes atts, TagType tagtype, out bool ignored)
{
+ ignored = false;
if (isApplication) {
if (String.Compare (tagid, "object", true, CultureInfo.InvariantCulture) != 0)
throw new ParseException (location, "Invalid tag for application file.");
ControlBuilder parent = stack.Builder;
ControlBuilder builder = null;
+ if (parent != null && parent.ControlType == typeof (HtmlTable) &&
+ (String.Compare (tagid, "thead", true, CultureInfo.InvariantCulture) == 0 ||
+ String.Compare (tagid, "tbody", true, CultureInfo.InvariantCulture) == 0)) {
+ ignored = true;
+ return true;
+ }
+
Hashtable htable = (atts != null) ? atts.GetDictionary (null) : emptyHash;
if (stack.Count > 1) {
try {
}
}
- if (builder == null && atts != null && atts.IsRunAtServer ()) {
+ bool runatServer = atts != null && atts.IsRunAtServer ();
+ if (builder == null && runatServer) {
string id = htable ["id"] as string;
if (id != null && !CodeGenerator.IsValidLanguageIndependentIdentifier (id))
throw new ParseException (Location, "'" + id + "' is not a valid identifier");
try {
- builder = rootBuilder.CreateSubBuilder (tagid, htable, null, tparser, location);
+ builder = RootBuilder.CreateSubBuilder (tagid, htable, null, tparser, location);
} catch (TypeLoadException e) {
throw new ParseException (Location, "Type not found.", e);
+ } catch (HttpException e) {
+ CompilationException inner = e.InnerException as CompilationException;
+ if (inner != null)
+ throw inner;
+
+ throw new ParseException (Location, e.Message, e);
} catch (Exception e) {
throw new ParseException (Location, e.Message, e);
}
if (builder == null)
return false;
+ // This is as good as we can do for now - if the parsed location contains
+ // both expressions and code render blocks then we're out of luck...
+ string plainText = location.PlainText;
+ if (!runatServer && plainText.IndexOf ("<%$") == -1&& plainText.IndexOf ("<%") > -1)
+ return false;
#if NET_2_0
PageParserFilter pfilter = PageParserFilter;
if (pfilter != null && !pfilter.AllowControl (builder.ControlType, builder))
} else {
if (!isApplication && builder is ObjectTagBuilder) {
ObjectTagBuilder ot = (ObjectTagBuilder) builder;
- if (ot.Scope != null && ot.Scope != "")
+ if (ot.Scope != null && ot.Scope.Length > 0)
throw new ParseException (location, "Scope not allowed here");
if (tagtype == TagType.Tag) {
CheckLanguage (language);
string src = (string) attributes ["src"];
if (src != null) {
- if (src == "")
+ if (src.Length == 0)
throw new ParseException (Parser,
"src cannot be an empty string");
}
return true;
}
+
+ if (current.ControlType == typeof (HtmlTable) && String.Compare (tagid, "thead", true, CultureInfo.InvariantCulture) == 0)
+ return true;
if (0 != String.Compare (tagid, btag, true, CultureInfo.InvariantCulture))
return false;
CodeConstructType MapTagTypeToConstructType (TagType tagtype)
{
switch (tagtype) {
- case TagType.DataBinding:
+ case TagType.CodeRenderExpression:
return CodeConstructType.ExpressionSnippet;
case TagType.CodeRender:
return CodeConstructType.CodeSnippet;
- case TagType.CodeRenderExpression:
+ case TagType.DataBinding:
return CodeConstructType.DataBindingSnippet;
default:
{
#if NET_2_0
PageParserFilter pfilter = PageParserFilter;
- if (pfilter != null && (!pfilter.AllowCode || !pfilter.ProcessCodeConstruct (MapTagTypeToConstructType (tagtype), code)))
+ // LAMESPEC:
+ //
+ // http://msdn.microsoft.com/en-us/library/system.web.ui.pageparserfilter.processcodeconstruct.aspx
+ //
+ // The above page says if false is returned then we should NOT process the
+ // code further, wheras in reality it's the other way around. The
+ // ProcessCodeConstruct return value means whether or not the filter
+ // _processed_ the code.
+ //
+ if (pfilter != null && (!pfilter.AllowCode || pfilter.ProcessCodeConstruct (MapTagTypeToConstructType (tagtype), code)))
return true;
#endif
ControlBuilder b = null;
return;
#if NET_2_0
- CompilationSection section = (CompilationSection) WebConfigurationManager.GetSection ("system.web/compilation");
+ CompilationSection section = (CompilationSection) WebConfigurationManager.GetWebApplicationSection ("system.web/compilation");
if (section.Compilers[tparser.Language] != section.Compilers[lang])
#else
CompilationConfiguration cfg = CompilationConfiguration.GetInstance (HttpContext.Current);
{
string str;
ControlBuilder builder;
-
+ AspGenerator generator;
+
public CodeRenderParser (string str, ControlBuilder builder)
{
this.str = str;
this.builder = builder;
}
- public void AddChildren ()
+ public void AddChildren (AspGenerator generator)
{
+ this.generator = generator;
int index = str.IndexOf ("<%");
- if (index > 0) {
- TextParsed (null, str.Substring (0, index));
- str = str.Substring (index);
- }
+ if (index > 0)
+ DoParseExpressions (str);
+ else
+ DoParse (str);
+ }
- AspParser parser = new AspParser ("@@inner_string@@", new StringReader (str));
+ void DoParseExpressions (string str)
+ {
+ int startIndex = 0, index = 0;
+ Regex codeDirective = new Regex ("(<%(?!@)(?<code>.*?)%>)|(<[\\w:\\.]+.*?runat=[\"']?server[\"']?.*?/>)",
+ RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.CultureInvariant);
+ Match match;
+ int strLen = str.Length;
+
+ while (index > -1 && startIndex < strLen) {
+ match = codeDirective.Match (str, index);
+
+ if (match.Success) {
+ string value = match.Value;
+ index = match.Index;
+ if (index > startIndex)
+ TextParsed (null, str.Substring (startIndex, index - startIndex));
+ DoParse (value);
+ index += value.Length;
+ startIndex = index;
+ } else
+ break;
+
+ if (index < strLen)
+ index = str.IndexOf ('<', index);
+ else
+ break;
+ }
+
+ if (startIndex < strLen)
+ TextParsed (null, str.Substring (startIndex));
+ }
+
+ void DoParse (string str)
+ {
+ AspParser parser = new AspParser ("@@nested_tag@@", new StringReader (str));
parser.Error += new ParseErrorHandler (ParseError);
parser.TagParsed += new TagParsedHandler (TagParsed);
parser.TextParsed += new TextParsedHandler (TextParsed);
void TagParsed (ILocation location, TagType tagtype, string tagid, TagAttributes attributes)
{
- if (tagtype == TagType.CodeRender)
- builder.AppendSubBuilder (new CodeRenderBuilder (tagid, false, location));
- else if (tagtype == TagType.CodeRenderExpression)
- builder.AppendSubBuilder (new CodeRenderBuilder (tagid, true, location));
- else if (tagtype == TagType.DataBinding)
- builder.AppendSubBuilder (new DataBindingBuilder (tagid, location));
- else
- builder.AppendLiteralString (location.PlainText);
+ switch (tagtype) {
+ case TagType.CodeRender:
+ builder.AppendSubBuilder (new CodeRenderBuilder (tagid, false, location));
+ break;
+
+ case TagType.CodeRenderExpression:
+ builder.AppendSubBuilder (new CodeRenderBuilder (tagid, true, location));
+ break;
+
+ case TagType.DataBinding:
+ builder.AppendSubBuilder (new DataBindingBuilder (tagid, location));
+ break;
+
+ case TagType.Tag:
+ case TagType.SelfClosing:
+ case TagType.Close:
+ if (generator != null)
+ generator.TagParsed (location, tagtype, tagid, attributes);
+ else
+ goto default;
+ break;
+
+ default:
+ string text = location.PlainText;
+ if (text != null && text.Trim ().Length > 0)
+ builder.AppendLiteralString (text);
+ break;
+ }
}
void TextParsed (ILocation location, string text)