Created
May 16, 2016 09:06
-
-
Save antmrdevlabs/10cabfdb412ded2fe519b69a0ac5f972 to your computer and use it in GitHub Desktop.
C# TemplateParser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using System.Collections.Generic; | |
using System.Linq; | |
using System.Text; | |
using System.Text.RegularExpressions; | |
using App.Infrastructure; | |
using System.Reflection; | |
using System.IO; | |
namespace App.ServiceModule.Internal | |
{ | |
public static class TemplateService | |
{ | |
public class TemplateParser | |
{ | |
private Session _Session; | |
private bool _exprVisited; | |
private bool _shouldStepOverBlock; | |
public string LayoutFile { get; set; } | |
public string PageFile { get; set; } | |
public List<string> LeadingObjects { get; private set; } | |
public string Form { get; private set; } | |
public List<string> FormContents { get; private set; } | |
/// <summary> | |
/// TemplateParser (c'tor) | |
/// </summary> | |
/// <param name="sessionId"></param> | |
public TemplateParser(Session session) | |
{ | |
_Session = session; | |
if (_Session != null) | |
{ | |
_Session.Form = null; | |
_Session.FormContents = null; | |
_Session.LeadingObjects = null; | |
} | |
this.LeadingObjects = new List<string>(); | |
this.FormContents = new List<string>(); | |
} | |
/// <summary> | |
/// Parse | |
/// </summary> | |
/// <returns></returns> | |
public string Parse() | |
{ | |
string output = ""; | |
string layoutHtml = ""; | |
string pageHtml = ""; | |
bool withLayout = !String.IsNullOrEmpty(this.LayoutFile); | |
if (withLayout) | |
{ | |
//Parse Layout | |
layoutHtml = this.Parse(this.LayoutFile); | |
} | |
//Parse Href | |
pageHtml = this.Parse(this.PageFile); | |
if (withLayout) | |
{ | |
//replace page placeholder in layout with parsed page content | |
output = Regex.Replace(layoutHtml, "{{page}}", pageHtml); | |
} | |
else | |
{ | |
output = pageHtml; | |
} | |
return output; | |
} | |
/// <summary> | |
/// Parse | |
/// </summary> | |
/// <param name="filePath"></param> | |
/// <returns></returns> | |
public string Parse(string filePath) | |
{ | |
int counter = 0; | |
string unchangedLine, line; | |
string parsedFile = ""; | |
bool isCondition = false; | |
// Read the file and display it line by line. | |
using (var fileStream = new System.IO.StreamReader(filePath)) | |
{ | |
while ((line = fileStream.ReadLine()) != null) | |
{ | |
if (!line.Contains("<!--") && !line.Contains("-->")) | |
{ | |
unchangedLine = line; | |
line = line.Trim().ToLower(); | |
Regex regexLeft = new Regex(@"\{\{"); | |
Regex regexRight = new Regex(@"\}\}"); | |
MatchCollection matchesLeftSide = regexLeft.Matches(line); | |
MatchCollection matchesRightSide = regexRight.Matches(line); | |
isCondition = this._CheckForCondition(unchangedLine); | |
if (isCondition || _shouldStepOverBlock) | |
{ | |
// do nothing | |
if (!_exprVisited) | |
this._ParseExpression(unchangedLine); | |
} | |
else | |
{ | |
if (matchesRightSide.Count != matchesLeftSide.Count) | |
{ | |
string exceptionMessage = String.Format("Template Parsing Error: Count of opening and closing Brackets in {0} Line {1} are not equal.", filePath, counter); | |
throw new ArgumentException(exceptionMessage); | |
} | |
else if (matchesLeftSide.Count > 0) | |
{ | |
if (!isCondition) | |
{ | |
foreach (Match leftMatch in matchesLeftSide) | |
{ | |
int startPos = leftMatch.Index; | |
Match rightMatch = regexRight.Match(line, startPos); | |
string tplExpr = line.Substring(startPos, rightMatch.Index + 2); | |
string tplExprValue = tplExpr.Replace("{{", "").Replace("}}", ""); | |
// parse expression value | |
// [0] => Type | |
// [1] => src | |
string path = ""; | |
string fileFileSystem = ""; | |
List<string> exprValueParams = tplExprValue.Split(' ').ToList(); | |
string paramClass = ""; | |
bool paramReplaceable = false; | |
bool includeHasTypeDefinition = false; | |
string includeTypeDefinition = ""; | |
string includeTypeSrcValue = ""; | |
if (exprValueParams.Count > 0) | |
{ | |
paramClass = exprValueParams.First(); | |
exprValueParams.Remove(paramClass); | |
} | |
foreach (string param in exprValueParams) | |
{ | |
if (paramClass == "control") | |
{ | |
path = Parameter.HtmlDirectory + @"Views\Common\Controls\"; | |
paramReplaceable = true; | |
if (param.Contains("src")) | |
{ | |
string [] srcParams = param.Split('='); | |
string srcValue = srcParams[1].Replace("\"", ""); | |
if (srcValue.Contains('/')) | |
{ | |
fileFileSystem = srcValue + ".html"; | |
} | |
else | |
{ | |
fileFileSystem = path + srcValue + ".html"; | |
} | |
} | |
} | |
else if (paramClass == "include") | |
{ | |
paramReplaceable = true; | |
if (exprValueParams.Count > 1) | |
{ | |
includeHasTypeDefinition = exprValueParams.Where(x => x.Contains("type")).Count() == 1; | |
} | |
if (includeHasTypeDefinition) | |
{ | |
if (param.Contains("type")) | |
{ | |
string[] typeDefParams = param.Split('='); | |
string typeDefValue = typeDefParams[1].Replace("\"", ""); | |
includeTypeDefinition = typeDefValue; | |
} | |
if (param.Contains("src")) | |
{ | |
string[] srcParams = param.Split('='); | |
includeTypeSrcValue = srcParams[1].Replace("\"", ""); | |
} | |
} | |
else | |
{ | |
string exceptionMessage = String.Format("Template Parsing Error: Include Statement has not type definition (type=\"\" is missing), line {0} in {1}", filePath, counter); | |
throw new ArgumentException(exceptionMessage); | |
} | |
} | |
} | |
if (paramReplaceable) | |
{ | |
string html = ""; | |
if (paramClass == "control") | |
html = this.Parse(fileFileSystem); //File.ReadAllText(fileFileSystem); | |
else | |
{ | |
string fileExt = ""; | |
switch (includeTypeDefinition) | |
{ | |
case "viewmodel": | |
includeTypeSrcValue = @"/viewmodels/" + includeTypeSrcValue; | |
fileExt = ".js"; | |
break; | |
case "script": | |
includeTypeSrcValue = @"/scripts/" + includeTypeSrcValue; | |
fileExt = ".js"; | |
break; | |
case "css": | |
includeTypeSrcValue = @"/css/" + includeTypeSrcValue; | |
fileExt = ".css"; | |
break; | |
default: | |
includeTypeSrcValue = @"/viewmodels/" + includeTypeSrcValue; | |
fileExt = ".js"; | |
break; | |
} | |
if (includeTypeDefinition == "css") | |
{ | |
html = String.Format("<link rel=\"stylesheet\" type=\"text/css\" href=\"{0}__filedate__{1}\"/>", includeTypeSrcValue, fileExt); | |
} | |
else | |
{ | |
// ViewModels shouldn't be cached on clientside, so no __FILEDATE__ extension | |
if (includeTypeDefinition == "viewmodel") | |
{ | |
html = String.Format("<script src=\"{0}{1}\" type=\"text/javascript\"></script>", includeTypeSrcValue,fileExt); | |
} | |
else | |
{ | |
html = String.Format("<script src=\"{0}__filedate__{1}\" type=\"text/javascript\"></script>", includeTypeSrcValue,fileExt); | |
} | |
} | |
} | |
line = Regex.Replace(line, tplExpr, this._ParseFiledateExpression(html)); | |
paramReplaceable = false; | |
} | |
parsedFile += line; | |
} | |
} | |
counter++; | |
} | |
else // No TemplateExpression found | |
{ | |
bool tagFound = false; | |
bool isAuthenticated = (_Session != null); | |
// if you want to replace the line with s.th. that | |
// is affected by the tags, set writeLine to false, but only(!!!) for | |
// the specific tag. that means in the specific if-clause, not at this point | |
// because the following line affects all lines and, if set to false will | |
// crash your HTML template | |
bool writeLine = true; | |
// check for tags: | |
// form and contents are properties used by authorization | |
if (line.Contains("frm-content-cv")) | |
{ | |
tagFound = true; | |
string parsedValue = this.ParseTag("frm-content-cv", line); | |
if (!String.IsNullOrEmpty(parsedValue)) | |
{ | |
this.FormContents.Add(parsedValue); | |
} | |
} | |
// form and contents are properties used by authorization | |
if (line.Contains("frm-cv")) | |
{ | |
tagFound = true; | |
this.Form = this.ParseTag("frm-cv", line); | |
} | |
// LeadingObjects are used for our context-based navigation | |
if (line.Contains("leading-object-cv")) | |
{ | |
tagFound = true; | |
string parsedValue = this.ParseTag("leading-object-cv", line); | |
if (!String.IsNullOrEmpty(parsedValue)) | |
{ | |
this.LeadingObjects.Add(parsedValue); | |
} | |
} | |
if (isAuthenticated) | |
{ | |
// add found tags to session object | |
_Session.FormContents = this.FormContents; | |
_Session.Form = this.Form; | |
_Session.LeadingObjects = this.LeadingObjects; | |
} | |
if (line.Contains("__filedate__")) | |
{ | |
string parsedLine = this._ParseFiledateExpression(line); | |
parsedFile += parsedLine; | |
} | |
else if (!tagFound || writeLine) | |
{ | |
// no tags found use unchangedLine | |
parsedFile += unchangedLine; | |
} | |
} | |
} | |
} | |
else | |
{ | |
parsedFile += line; | |
} | |
} | |
fileStream.Close(); | |
return parsedFile; | |
} | |
} | |
/// <summary> | |
/// _CheckForCondition | |
/// </summary> | |
/// <param name="line"></param> | |
/// <returns></returns> | |
private bool _CheckForCondition(string line) | |
{ | |
string[] openingExprs = new[] { "IF" }; | |
bool ret = false; | |
foreach (var oe in openingExprs) | |
{ | |
if (line.Contains(oe) && line.Contains("{{") && line.Contains("}}")) | |
{ | |
string plainExp = line.Replace("{{", "").Replace("}}", "").Replace('"', ' ').Trim(); | |
foreach (var expr in openingExprs) | |
{ | |
if (plainExp.StartsWith("/" + expr)) | |
{ | |
_exprVisited = false; | |
} | |
} | |
ret = true; | |
break; | |
} | |
} | |
return ret; | |
} | |
private string _ParseFiledateExpression(string line) | |
{ | |
string file = null; | |
int startpos = 0; | |
string filetype = null; | |
Match filetypeMatch = Regex.Match(line, @"(\.css)|(\.js)|(\.png)|(\.jpg)|(\.ico)", RegexOptions.IgnoreCase); | |
if (filetypeMatch.Success) | |
{ | |
filetype = line.Substring(filetypeMatch.Index, filetypeMatch.Length); | |
} | |
if (!String.IsNullOrEmpty(filetype)) | |
{ | |
if (line.Contains("href")) | |
{ | |
startpos = line.IndexOf("href=\"/") + ("href=\"/").Length; | |
file = line.Substring(startpos, line.IndexOf(filetype + "\"") - startpos) + filetype; | |
} | |
else if (line.Contains("src")) | |
{ | |
startpos = line.IndexOf("src=\"/") + ("src=\"/").Length; | |
file = line.Substring(startpos, line.IndexOf(filetype + "\"") - startpos) + filetype; | |
} | |
if (!String.IsNullOrEmpty(file)) | |
{ | |
var filePath = Parameter.HtmlDirectory + file.Replace("/", "\\"); | |
var rawFilePath = filePath.Replace("__filedate__", ""); | |
var lastWriteTime = ""; | |
if (File.Exists(rawFilePath)) | |
{ | |
lastWriteTime = File.GetLastWriteTime(rawFilePath).ToFileTime().ToString(); | |
} | |
line = line.Replace("filedate", lastWriteTime); | |
} | |
} | |
return line; | |
} | |
/// <summary> | |
/// _ParseExpression | |
/// </summary> | |
/// <param name="line"></param> | |
public void _ParseExpression(string line) | |
{ | |
string[] openingExprs = new[] { "IF" }; | |
string plainExp = line.Replace("{{", "").Replace("}}", "").Replace('"', ' ').Trim(); | |
//if="session.isauthenticated" | |
if (plainExp.Contains('=')) | |
{ | |
string condition = plainExp.Split('=').ElementAt(1); | |
string className = ""; | |
string methodName = ""; | |
object ret = default(object); | |
if (condition.Contains('.')) | |
{ | |
var tmp = condition.Split('.'); | |
className = tmp[0].Trim(); | |
if (tmp.Length == 2) | |
methodName = tmp[1].Trim(); | |
} | |
if (className.Contains("Service")) | |
{ | |
ret = Tools.CallAnonymousMethod(Assembly.GetExecutingAssembly(), "App.ServiceModule", className, methodName, _Session); | |
} | |
else if (className == "Session") | |
{ | |
object prop = _Session.GetProperty(methodName); | |
} | |
_shouldStepOverBlock = !(bool)ret; | |
_exprVisited = true; | |
} | |
else | |
{ | |
// {{/IF}} | |
// we are at the end of our conditional expression | |
// the following blocks should treated as normal blocks | |
// so reset values: | |
_shouldStepOverBlock = false; | |
_exprVisited = false; | |
} | |
} | |
/// <summary> | |
/// ParseTag | |
/// </summary> | |
/// <param name="tag"></param> | |
/// <param name="line"></param> | |
/// <returns></returns> | |
public string ParseTag(string tag, string line) | |
{ | |
// example | |
// tag: frm-cv | |
// line: <section id="content" class="container-fluid" leading-object-cv="<entity name>" frm-cv="<form name>"> | |
int startPosTag = line.IndexOf(tag, 0); | |
int endPosTag = line.IndexOf('=', startPosTag); | |
int quoteCount = 0; | |
string val = ""; | |
foreach (var chr in line.Substring(endPosTag).ToCharArray()) | |
{ | |
if (chr == '"') | |
{ | |
quoteCount++; | |
} | |
if (quoteCount < 2) | |
{ | |
if (chr != '"') | |
val += chr.ToString(); | |
else | |
val = null; | |
} | |
} | |
return val; | |
} | |
} | |
public static string Parse(Session session, string layoutFile, string pageFileSystem) | |
{ | |
TemplateParser tp = new TemplateParser(session); | |
// set layout | |
tp.LayoutFile = layoutFile; | |
tp.PageFile = pageFileSystem; | |
return tp.Parse(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment