Skip to content

Instantly share code, notes, and snippets.

@antmrdevlabs
Created May 16, 2016 09:06
Show Gist options
  • Save antmrdevlabs/10cabfdb412ded2fe519b69a0ac5f972 to your computer and use it in GitHub Desktop.
Save antmrdevlabs/10cabfdb412ded2fe519b69a0ac5f972 to your computer and use it in GitHub Desktop.
C# TemplateParser
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