using System;
using System.Text;
using System.IO;
using System.Collections;
using System.Collections.Specialized;
using System.Text.RegularExpressions;
using System.Web.RegularExpressions;
using AnticipatingMinds.Genesis.CodeDOM;
using AnticipatingMinds.Genesis.AspNetDom;
using AnticipatingMinds.Genesis.CodeDOM.Utilities;
namespace AnticipatingMinds.Genesis.CodeParser{
/// <summary>
/// Summary description for AspNetParser.
/// </summary>
[System.Security.Permissions.StrongNameIdentityPermissionAttribute(System.Security.Permissions.SecurityAction.LinkDemand,
PublicKey=
"00240000048000009400000006020000002400005253413100040000010001009D309779C258129573FC313836474C75C4CE9F4" +
"107524FA0B9A6DB2E52754459C5A8946E4CBD5B98ACDB2413C5AFD38C1DF00C9A946713E867237B47F9D9CC473D4A853EACBEAB" +
"799EC0A271B468D4B6D52301A414A7772F05FEBD2BA7D0A2835F0D45E401C3C37F9E7B991D29F07DA88E20BB3839A34A2739AB6" +
"56B5204C8BC")]
public class AspNetParser : CodeParser
{
private class TagAttribute
{
public string Name;
public string Value;
public int NamePosition;
public int ValuePoisition;
}
public AspNetParser(string sourceCode,string fileName,CodeAssembly codeAssembly):base(sourceCode,fileName,codeAssembly)
{
this.sourceCode = sourceCode;
}
public override AnticipatingMinds.Genesis.CodeDOM.CodeAssemblyFile Parse(System.Threading.ManualResetEvent cancelEvent)
{
aspNetAssemblyFile = new AspNetAssemblyFile();
aspNetAssemblyFile.Assembly = CodeAssembly;
aspNetAssemblyFile.Language = CodeLanguage.AspNet;
aspNetAssemblyFile.SourceFileName = FileName;
aspNetAssemblyFile.Namespace.DeclaredTypes.Add(aspNetClassDeclaration);
aspNetClassDeclaration.Name = GetAspClassNameFromFileName(FileName);
aspNetClassDeclaration.SourcePosition = new CodeElementPosition(FileName,0,0,0);
registeredNamespacePrefixes.Add("asp","System.Web.UI.WebControls");
ParseAspNetElement();
ParseRenderCodeBlocks(cancelEvent);
ParseExpressions(cancelEvent);
ParseScriptBlocks(cancelEvent);
CodeCompileUnitUtils.RefreshCompileUnitElementsReferences(aspNetAssemblyFile);
return aspNetAssemblyFile;
}
private void ParseRenderCodeBlocks(System.Threading.ManualResetEvent cancelEvent)
{
//Now lets parse snippets of code.
StringBuilder renderingCode = new StringBuilder();
renderingCode.Append('{');
int lastPosition = 1;
int lastLine = 1;
int lastColumn = 1;
foreach(CodeStatementSnippet codeSnippet in renderingSnippets)
{
int snippetPosition = (int) snippetPositions[codeSnippet];
int line = 1;
int column = 1;
GetPositionLineAndColumn(snippetPosition,out line,out column);
//append line symbols
//reset last column if lines are different.
if(line != lastLine)
{
//pad first:
renderingCode.Append(' ',snippetPosition-lastPosition - (line - lastLine) - (column-1));
if(line - lastLine > 0)
renderingCode.Append('\r',line - lastLine);
if(column-1 > 0)
renderingCode.Append(' ',column-1);
}
else
{
renderingCode.Append(' ',snippetPosition-lastPosition);
}
lastLine = line;
lastColumn = column;
lastPosition = snippetPosition + codeSnippet.Text.Length;
renderingCode.Append(codeSnippet.Text);
renderingCode.Append(';'); // append empty statement in between every snippet
lastPosition++;
}
renderingCode.Append('}');
CSharpParser parser = new CSharpParser(renderingCode.ToString(),FileName,CodeAssembly);
CodeStatementBlock statmentsBlock = new CodeStatementBlock();
try
{
statmentsBlock = parser.ParseStatements(cancelEvent);
}
catch
{
//Ignore parse exceptions - they are not that important in Aspx code.
}
aspNetClassDeclaration.RenderStatements.AddRange(statmentsBlock.Statements);
}
private void ParseExpressions(System.Threading.ManualResetEvent cancelEvent)
{
foreach(CodeStatementSnippet expressionCode in expressionSnippets)
{
//Now lets parse snippets of code.
StringBuilder renderingCode = new StringBuilder();
int snippetPosition = (int) snippetPositions[expressionCode];
int line = 1;
int column = 1;
GetPositionLineAndColumn(snippetPosition,out line,out column);
//append line symbols
//pad first:
renderingCode.Append(' ',snippetPosition - (line-1) - (column-1));
if(line > 1)
renderingCode.Append('\r',line - 1);
if(column > 1)
renderingCode.Append(' ',column -1);
renderingCode.Append(expressionCode.Text);
CSharpParser parser = new CSharpParser(renderingCode.ToString(),FileName,CodeAssembly);
CodeExpression expression = null;
try
{
expression = parser.ParseExpression(cancelEvent);
}
catch
{
//Ignore parse exceptions - they are not that important in Aspx code.
}
aspNetClassDeclaration.EvaluatedExpressions.Add(expression);
}
}
private void ParseScriptBlocks(System.Threading.ManualResetEvent cancelEvent)
{
foreach(CodeStatementSnippet scriptCode in scriptSnippets)
{
//Now lets parse snippets of code.
StringBuilder renderingCode = new StringBuilder();
renderingCode.Append("class A{");
int snippetPosition = (int) snippetPositions[scriptCode] - 8;
int line = 1;
int column = 1;
GetPositionLineAndColumn(snippetPosition,out line,out column);
//append line symbols
//pad first:
renderingCode.Append(' ',snippetPosition - (line-1) - (column-1));
if(line > 1)
renderingCode.Append('\r',line - 1);
if(column > 1)
renderingCode.Append(' ',column -1);
renderingCode.Append(scriptCode.Text);
renderingCode.Append(" }");
CSharpParser parser = new CSharpParser(renderingCode.ToString(),FileName,CodeAssembly);
try
{
aspNetClassDeclaration.Members.AddRange( ((parser.Parse(cancelEvent) as CodeCompileUnit).Namespace.DeclaredTypes[0] as CodeClassDeclaration).Members);
}
catch
{
//Ignore parse exceptions - they are not that important in Aspx code.
}
}
}
public override System.CodeDom.Compiler.CompilerErrorCollection Errors
{
get
{
return errors;
}
}
#region ASP Parsing
private static Regex tagRegex = new TagRegex();
private static Regex endtagRegex = new EndTagRegex();
private static Regex textRegex = new TextRegex();
private static Regex directiveRegex = new DirectiveRegex();
private static Regex aspCodeRegex = new AspCodeRegex();
private static Regex aspExprRegex = new AspExprRegex();
private static Regex databindExprRegex = new DatabindExprRegex();
private static Regex attributeDatabindExprRegex = new DataBindRegex();
private static Regex serverCommentRegex = new CommentRegex();
private static Regex xmlCommentRegex = new Regex(@"\G<!--(([^-]*)-)*?->", RegexOptions.Multiline | RegexOptions.Compiled);
private static Regex includeRegex = new IncludeRegex();
private static Regex gtRegex = new GTRegex();
private static Regex ltRegex = new LTRegex();
private static Regex serverTagsRegex = new ServerTagsRegex();
private static Regex runatServerRegex = new RunatServerRegex();
bool inScriptTag = false;
bool closeTagDoesNotCloseScript;
private void ParseAspNetElement()
{
string text = SourceCode;
int offset = 0;
Match match;
int scriptStartPosition = -1;
do
{
//Skip text - we are not intrested in text.
match = textRegex.Match(text, offset);
if( match.Success )
{
offset = (match.Index + match.Length);
}
if( offset == text.Length )
{
break;
}
this.closeTagDoesNotCloseScript = false;
if( ! this.inScriptTag )
{
match = directiveRegex.Match(text, offset);
if( match.Success )
{
this.ProcessDirective(match);
goto MOVE_OFFSET;
}
}
// match = includeRegex.Match(text, offset);
// if( match.Success )
// {
// //this.ProcessServerInclude(match);
// }
// else
{
match = serverCommentRegex.Match(text, offset);
if( match.Success )
{
goto MOVE_OFFSET;
}
match = xmlCommentRegex.Match(text, offset);
if( match.Success )
{
goto MOVE_OFFSET;
}
if( this.inScriptTag == false )
{
match = aspExprRegex.Match(text, offset);
if( match.Success )
{
string code = SourceCode.Substring(match.Index,match.Length);
int codeStartedPosition = code.IndexOf("=");
if(codeStartedPosition != -1)
{
code = code.Substring(codeStartedPosition+1,code.LastIndexOf('%')-(codeStartedPosition+1));
CodeStatementSnippet snippet = new CodeStatementSnippet(code);
expressionSnippets.Add(snippet);
snippetPositions[snippet] = match.Index;
}
goto MOVE_OFFSET;
}
match = databindExprRegex.Match(text, offset);
if( match.Success )
{
string code = SourceCode.Substring(match.Index,match.Length);
int codeStartedPosition = code.IndexOf("#");
if(codeStartedPosition != -1)
{
code = code.Substring(codeStartedPosition+1,code.LastIndexOf('%') - (codeStartedPosition+1));
CodeStatementSnippet snippet = new CodeStatementSnippet(code);
expressionSnippets.Add(snippet);
snippetPositions[snippet] = match.Index;
}
goto MOVE_OFFSET;
}
match = aspCodeRegex.Match(text, offset);
if( match.Success )
{
string code = SourceCode.Substring(match.Index,match.Length);
int codeStartedPosition = code.IndexOf("%");
if(codeStartedPosition != -1)
{
code = code.Substring(codeStartedPosition+1,code.LastIndexOf('%') - (codeStartedPosition+1));
CodeStatementSnippet snippet = new CodeStatementSnippet(code);
renderingSnippets.Add(snippet);
snippetPositions[snippet] = match.Index + codeStartedPosition+1;
}
goto MOVE_OFFSET;
}
match = tagRegex.Match(text, offset);
if( match.Success )
{
string tagName = match.Groups["tagname"].Value;
IDictionary attributes = ProcessAttributes(match);
//Process script block
if(String.Compare(tagName,"script",true) == 0 &&
attributes.Contains("runat") &&
String.Compare("server",(attributes["runat"] as TagAttribute).Value,true) == 0)
{
scriptStartPosition = match.Index + match.Value.Length;
inScriptTag = true;
goto MOVE_OFFSET;
}
//Process server controls
if((tagName.IndexOf(":") != -1) || (attributes.Contains("runat") &&
String.Compare("server",(attributes["runat"] as TagAttribute).Value,true) == 0))
{
AspNetServerControl serverControl = new AspNetServerControl();
RecordElementPosition(serverControl,match.Index);
serverControl.Name = tagName;
//Is it user control?
if(userControlTagNames.Contains(serverControl.Name))
{
serverControl.ControlType = new CodeTypeReference(userControlTagNames[serverControl.Name] as string);
}
else
{
//Server control or HTML control?
if(tagName.IndexOf(":") != -1)
{
string namespacePrefix = tagName.Substring(0,tagName.IndexOf(":"));
string typeName = tagName.Substring(tagName.IndexOf(":")+1);
string controlTypeName = "";
if(registeredNamespacePrefixes.Contains(namespacePrefix))
controlTypeName = registeredNamespacePrefixes[namespacePrefix] + ".";
controlTypeName += typeName;
serverControl.ControlType = new CodeTypeReference(controlTypeName);
}
else
{
switch(tagName.ToLower())
{
case "a":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlAnchor");
break;
}
case "button":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlButton");
break;
}
case "form":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlForm");
break;
}
case "img":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlImage");
break;
}
case "input":
{
string type = "";
if(attributes.Contains("type"))
type = attributes["type"] as String;
switch(type)
{
case "button":
case "submit":
case "reset":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlInputButton");
break;
}
case "checkbox":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlInputCheckBox");
break;
}
case "file":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlInputFile");
break;
}
case "hidden":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlInputHidden");
break;
}
case "image":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlInputImage");
break;
}
case "radio":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlInputRadioButton");
break;
}
case "password":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlInputText");
break;
}
default:
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlInputControl");
break;
}
}
break;
}
case "select":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlSelect");
break;
}
case "table":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlTable");
break;
}
case "td":
case "th":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlTableCell");
break;
}
case "tr":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlTableRow");
break;
}
case "textarea":
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlTextArea");
break;
}
default:
{
serverControl.ControlType = new CodeTypeReference("System.Web.UI.HtmlControls.HtmlGenericControl");
break;
}
}
}
}
foreach(TagAttribute a in attributes.Values)
{
AspNetTagAttribute tagAttribute = new AspNetTagAttribute();
RecordElementPosition(tagAttribute,a.NamePosition);
tagAttribute.Name = new CodeUnresolvedReferenceExpression(a.Name);
RecordElementPosition(tagAttribute.Name,a.NamePosition);
tagAttribute.Value = new CodeUnresolvedReferenceExpression(a.Value);
RecordElementPosition(tagAttribute.Value,a.ValuePoisition);
serverControl.Attributes.Add(tagAttribute);
}
if(webControlsStack.Count != 0)
(webControlsStack.Peek() as AspNetServerControl).NestedTags.Add(serverControl);
else
aspNetClassDeclaration.ServerControls.Add(serverControl);
Group emptyTag = match.Groups["empty"];
if( !emptyTag.Success )
{
webControlsStack.Push(serverControl);
}
}
goto MOVE_OFFSET;
}
}
match = endtagRegex.Match(text, offset);
if( match.Success )
{
IDictionary attributes = ProcessAttributes(match);
string tagName = match.Groups["tagname"].Value;
if( IsScriptTagName(tagName) && inScriptTag)
{
string code = SourceCode.Substring(scriptStartPosition,match.Index - scriptStartPosition);
CodeStatementSnippet snippet = new CodeStatementSnippet(code);
scriptSnippets.Add(snippet);
snippetPositions[snippet] = scriptStartPosition;
scriptStartPosition = -1;
inScriptTag = false;
}
//Process server controls closing tag
if(tagName.IndexOf(":") != -1 && webControlsStack.Count != 0)
{
bool openTagFound = false;
foreach(AspNetServerControl control in webControlsStack)
if(String.Compare(control.Name,tagName,true) == 0)
{
openTagFound = true;
break;
}
if(openTagFound)
while(String.Compare((webControlsStack.Pop() as AspNetServerControl).Name,tagName,true) != 0)
{
;
}
}
}
}
MOVE_OFFSET:
if( (match == null) || (!match.Success || this.closeTagDoesNotCloseScript) )
{
offset = (offset + 1);
}
else
{
offset = (match.Index + match.Length);
}
}
while (offset != text.Length);
}
private bool IsScriptTagName(string name)
{
return (CaseInsensitiveComparer.Default.Compare("SCRIPT", name) == 0);
}
private void ProcessDirective(Match directiveMatch)
{
IDictionary directives = ProcessAttributes(directiveMatch);
if(directives.Contains("language"))
{
if(String.Compare((directives["language"] as TagAttribute).Value,"C#",true) == 0)
aspNetAssemblyFile.InlineCodeLanguage = CodeLanguage.CSharp;
}
if(directives.Contains("Inherits"))
{
CodeTypeReference typeReference = new CodeTypeReference((directives["Inherits"] as TagAttribute).Value);
RecordElementPosition(typeReference,(directives["Inherits"] as TagAttribute).ValuePoisition);
aspNetClassDeclaration.BaseTypes.Insert(0,typeReference);
}
if(directives.Contains("Register"))
{
String tagPrefix = (directives["TagPrefix"] as TagAttribute).Value;
if(directives.Contains("Namespace"))
{
//Namespace import
//Associate prefix with manespace.
string importedNamespace = (directives["Namespace"] as TagAttribute).Value;
if(!registeredNamespacePrefixes.Contains(tagPrefix))
registeredNamespacePrefixes.Add(tagPrefix,importedNamespace);
}
else
{
//User control import
//associate full control name (prefix:name) with type
string tagName = (directives["Tagname"] as TagAttribute).Value;
string src = (directives["src"] as TagAttribute).Value;
//Build the name of the file
String currentFileFolder = Path.GetDirectoryName(FileName);
if(src.Length > 0 && src[0] == '~')
{
currentFileFolder = Path.GetDirectoryName(CodeAssembly.ApplicationData["ProjectFileName"] as string);
src = src.Substring(1);
if(src[0] == '\\' || src[0] == '/')
src = src.Substring(1);
}
if(Directory.Exists(currentFileFolder))
{
Directory.SetCurrentDirectory(currentFileFolder);
string fullControlPath = Path.GetFullPath(src);
string controlTypeName = GetAspClassNameFromFileName(fullControlPath);
userControlTagNames[tagPrefix + ":" + tagName] = controlTypeName;
}
}
}
if(directives.Contains("Import"))
{
aspNetAssemblyFile.Namespace.Imports.Add(new CodeNamespaceImport((directives["namespace"] as TagAttribute).Value));
}
if(directives.Contains("Implements"))
{
CodeTypeReference typeReference = new CodeTypeReference((directives["interface"] as TagAttribute).Value);
RecordElementPosition(typeReference,(directives["interface"] as TagAttribute).ValuePoisition);
aspNetClassDeclaration.BaseTypes.Add(typeReference);
}
}
private IDictionary ProcessAttributes(Match match)
{
Hashtable attributes = new Hashtable(CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
CaptureCollection names = match.Groups["attrname"].Captures;
CaptureCollection values = match.Groups["attrval"].Captures;
for(int i = 0; i < names.Count; i ++)
{
Capture name = names[i];
Capture value = values[i];
TagAttribute attribute = new TagAttribute();
attribute.Name = name.Value;
attribute.Value = value.Value;
attribute.NamePosition = name.Index;
attribute.ValuePoisition = value.Index;
if(attributes.Contains(name.Value))
attributes[name.Value] = attribute;
else
attributes.Add(name.Value,attribute);
}
return attributes as IDictionary;
}
private void RecordElementPosition(CodeElement codeElment,int position)
{
codeElment.SourcePosition.Position = position;
codeElment.CompileUnit = aspNetAssemblyFile;
codeElment.SourcePosition.FileName = aspNetAssemblyFile.SourceFileName;
}
private void GetPositionLineAndColumn(int position,out int line, out int column)
{
line = 1;
column = 1;
int lastPosition = Math.Min(position,SourceCode.Length);
for(int i = 0; i < lastPosition; i++)
{
column++;
if(SourceCode[i] == 0x0A || SourceCode[i] == 0x2028 || SourceCode[i] == 0x2029)
{
line++;
column = 1;
}
}
}
private string GetAspClassNameFromFileName(string fileName)
{
return fileName.Replace('.','_').Replace('\\','_').Replace('/','_').Replace(':','_');
}
#endregion
private AspNetAssemblyFile aspNetAssemblyFile;
private AspNetClassDeclaration aspNetClassDeclaration = new AspNetClassDeclaration();
private System.CodeDom.Compiler.CompilerErrorCollection errors = new System.CodeDom.Compiler.CompilerErrorCollection();
private CodeStatementCollection scriptSnippets = new CodeStatementCollection();
private Hashtable registeredNamespacePrefixes = new Hashtable(CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
private Hashtable userControlTagNames = new Hashtable(CaseInsensitiveHashCodeProvider.Default, CaseInsensitiveComparer.Default);
private CodeStatementCollection expressionSnippets = new CodeStatementCollection();
private CodeStatementCollection renderingSnippets = new CodeStatementCollection();
private Hashtable snippetPositions = new Hashtable();
private Stack webControlsStack = new Stack();
private string sourceCode;
}
}
|