/*
* Box Socialâ„¢
* http://boxsocial.net/
* Copyright © 2007, David Lachlan Smith
*
* $Id:$
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
using System;
using System.IO;
using System.Collections.Generic;
using System.Resources;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Caching;
using BoxSocial.Forms;
namespace BoxSocial.IO
{
public struct TemplateVariable
{
public string Name;
public int Index;
public TemplateVariable(string name, int index)
{
this.Name = name;
this.Index = index;
}
}
public class VariableCollection
{
private string loopName;
private Dictionary> childLoops = new Dictionary>();
private Dictionary variables = new Dictionary();
private VariableCollection parentCollection = null;
internal VariableCollection()
{
loopName = String.Empty;
}
public VariableCollection(string name)
{
loopName = name;
}
public VariableCollection CreateChild(string name)
{
string fullName = (string.IsNullOrEmpty(loopName)) ? name : loopName + "." + name;
VariableCollection vc = new VariableCollection(fullName);
vc.parentCollection = this;
if (!ContainsLoop(fullName))
{
childLoops.Add(fullName, new List());
}
childLoops[fullName].Add(vc);
return vc;
}
public void Parse(string key, string value)
{
if (!variables.ContainsKey(key))
{
variables.Add(key, HttpUtility.HtmlEncode(value));
}
}
public void Parse(string key, long value)
{
if (!variables.ContainsKey(key))
{
variables.Add(key, HttpUtility.HtmlEncode(value.ToString()));
}
}
public void Parse(string key, long value, bool longNumberFormat)
{
if (!variables.ContainsKey(key))
{
if (longNumberFormat)
{
throw new NotImplementedException("Use Functions.LargeIntegerToString instead");
}
else
{
variables.Add(key, HttpUtility.HtmlEncode(value.ToString()));
}
}
}
public void Parse(string key, FormField formField)
{
if (formField.GetType().Assembly.GetName().Name == "BoxSocial.Forms" ||
formField.GetType().Assembly.GetName().Name == "BoxSocial.Internals")
{
if (!variables.ContainsKey(key))
{
variables.Add(key, formField.ToString());
}
}
}
///
/// Parse raw data to a template, only valid for Box Social internals
///
///
///
public void ParseRaw(string key, string value)
{
Assembly asm = Assembly.GetCallingAssembly();
if (asm.GetName().Name == "BoxSocial.Internals")
{
if (!variables.ContainsKey(key))
{
variables.Add(key, value);
}
}
}
internal void parseRaw(string key, string value)
{
if (!variables.ContainsKey(key))
{
variables.Add(key, value);
}
}
public void ParseVariables(string key, string value)
{
try
{
variables.Add(key, value);
}
catch
{
}
}
public void ParseVariables(Dictionary vars)
{
foreach (string key in vars.Keys)
{
variables.Add(key, vars[key]);
}
}
internal bool ContainsLoop(string name)
{
return childLoops.ContainsKey(name);
}
internal int LoopCount(string name)
{
return childLoops[name].Count;
}
internal string this[string key]
{
get
{
/*if (variables.ContainsKey(key))
{
return variables[key];
}
else
{
if (parentCollection != null)
{
return parentCollection[key];
}
}
return String.Empty;*/
string returnValue;
if (TryGetValue(key, out returnValue))
{
return returnValue;
}
else
{
return String.Empty;
}
}
}
internal bool ContainsKey(string key)
{
if (variables.ContainsKey(key))
{
return true;
}
else
{
if (parentCollection != null)
{
return parentCollection.ContainsKey(key);
}
}
return false;
}
internal bool TryGetValue(string key, out string value)
{
if (!variables.TryGetValue(key, out value))
{
if (parentCollection != null)
{
return parentCollection.TryGetValue(key, out value);
}
else
{
return false;
}
}
else
{
return true;
}
}
internal List GetChildCollection(string name)
{
return childLoops[name];
}
internal string Path
{
get
{
return loopName;
}
}
}
public class Template
{
private IProse prose;
protected VariableCollection variables = new VariableCollection();
private Dictionary pageAssembly = new Dictionary();
private string template;
//private Dictionary loopTemplates;
private string path;
private string templateName;
private string templateAssembly;
private static Object pathLock = new object();
private static string templatePath = null;
public static string Path
{
set
{
lock (pathLock)
{
if (templatePath == null)
{
templatePath = value;
}
}
}
get
{
return templatePath;
}
}
public void AddPageAssembly(Assembly value)
{
if (!pageAssembly.ContainsKey(value.GetName().Name))
{
pageAssembly.Add(value.GetName().Name, value);
}
}
public Template()
{
this.path = Path;
}
public Template(Assembly assembly, string templateName)
{
this.path = Path;
AddPageAssembly(assembly);
this.templateAssembly = assembly.GetName().Name;
this.templateName = templateName;
}
public Template(string fileName)
{
this.path = Path;
templateName = fileName;
}
public Template(string path, string fileName)
{
this.path = path;
templateName = fileName;
}
public void SetTemplate(string fileName)
{
templateAssembly = null;
templateName = fileName;
}
public void SetTemplate(string assembly, string fileName)
{
templateAssembly = assembly;
templateName = fileName;
}
// TODO: reconsider this into the constructor
public void SetProse(IProse prose)
{
this.prose = prose;
}
public void Parse(string key, string value)
{
variables.Parse(key, value);
}
public void Parse(string key, FormField formField)
{
if (formField.GetType().Assembly.GetName().Name == "BoxSocial.Forms" ||
formField.GetType().Assembly.GetName().Name == "BoxSocial.Internals")
{
variables.Parse(key, formField);
}
}
///
/// Parse raw data to a template, only valid for Box Social internals
///
///
///
public void ParseRaw(string key, string value)
{
Assembly asm = Assembly.GetCallingAssembly();
if (asm.GetName().Name == "BoxSocial.Internals")
{
variables.parseRaw(key, value);
}
}
public void ParseVariables(string key, string value)
{
variables.Parse(key, value);
}
public void ParseVariables(Dictionary vars)
{
variables.ParseVariables(vars);
}
public VariableCollection CreateChild(string name)
{
return variables.CreateChild(name);
}
public string LoadTemplateFile(string templateName)
{
string template;
if (templateAssembly != null && (!templateName.EndsWith(".html")))
{
try
{
ResourceManager rm;
switch (templateAssembly)
{
case "Groups":
case "Networks":
case "Musician":
rm = new ResourceManager("BoxSocial." + templateAssembly + ".Templates", pageAssembly[templateAssembly]);
break;
default:
rm = new ResourceManager("BoxSocial.Applications." + templateAssembly + ".Templates", pageAssembly[templateAssembly]);
break;
}
object templateObject = rm.GetObject(templateName);
if (templateObject is string)
{
template = (string)templateObject;
}
else if (templateObject is byte[])
{
template = System.Text.UTF8Encoding.UTF8.GetString((byte[])templateObject);
if (template.StartsWith("\xEF\xBB\xBF"))
{
template = template.Remove(0, 3);
}
if (template.StartsWith("\xBF"))
{
template = template.Remove(0, 1);
}
}
else
{
template = string.Format("Unknown template type {1} in assembly {0}",
templateAssembly, templateName);
}
}
catch (Exception ex)
{
template = string.Format("Could not load template {1} from assembly {0}.
Additionally the following exception was thrown.
{2}",
templateAssembly, templateName, ex.ToString().Replace("\n", "\n
"));
}
}
else
{
template = Template.OpenTextFile(System.IO.Path.Combine(path, templateName));
if (template == null)
{
template = string.Format("Could not load template {0}",
templateName);
}
}
return template;
}
///
/// ONLY PUT A SINGLE IF STATEMENT ON A SINGLE LINE OF xHTML
///
///
public override string ToString()
{
template = LoadTemplateFile(templateName);
StringBuilder output = new StringBuilder();
string[] lines = template.Replace("\r", String.Empty).Split('\n');
int lineAdjust = 0;
ProcessLines(lines, output, variables);
return output.ToString();
}
private void ProcessLines(string[] lines, StringBuilder output, VariableCollection variables)
{
ProcessLines(lines, output, variables, -1);
}
///
///
///
///
///
/// Path to the childs parent
/// Index of the child when doing a loop
private void ProcessLines(string[] lines, StringBuilder output, VariableCollection variables, int childIndex)
{
int inIf = 0; /* depth we are into an If block */
Stack inElse = new Stack(); /* depth within an else block */
Stack conditionTrue = new Stack(); /* keep track of wether the condition for the current block is true or false */
int rootFalse = 0;
string line;
bool inLoop = false;
string loopName = String.Empty;
List loopCache = new List();
for (int i = 0; i < lines.Length; i++)
{
line = lines[i];
/* loops */
if (!inLoop)
{
int iob = line.IndexOf("", iob);
//Match lm = Regex.Match(line, @"\<\!\-\- BEGIN ([A-Za-z0-9\.\-_]+) \-\-\>", RegexOptions.Compiled);
//if (lm.Success)
if (iobe >= 0)
{
inLoop = true;
//loopName = lm.Groups[1].Value;
loopName = line.Substring(iob + 11, iobe - (iob + 11));
loopCache.Clear();
//line = line.Remove(lm.Index, lm.Length);
line = line.Remove(iob, (iobe + 4) - iob);
continue;
}
}
}
else
{
int ioe = line.IndexOf("");
if (ioe >= 0)
{
//int ioee = line.IndexOf(loopName + " -->", ioe);
//Match lm = Regex.Match(line, @"\<\!\-\- END " + loopName + @" \-\-\>", RegexOptions.Compiled);
//if (lm.Success)
//if (ioee >= 0)
{
inLoop = false;
int l;
if (variables.ContainsLoop(loopName))
//if (loopVariables.ContainsKey(loopName))
{
for (l = 0; l < variables.GetChildCollection(loopName).Count; l++)
{
ProcessLines(loopCache.ToArray(), output, variables.GetChildCollection(loopName)[l], l);
}
}
//line = line.Remove(lm.Index, lm.Length);
line = line.Remove(ioe, loopName.Length + 9 + 4);
continue;
}
/*else
{
loopCache.Add(line);
continue;
}*/
}
else
{
loopCache.Add(line);
continue;
}
}
/* Conditional statements */
/*
* To ensure the proper start tag ends the proper end tag, all tag sets have to be matched
*/
//Match rm = null;
int ioi = line.IndexOf("", ioi);
//rm = Regex.Match(line, @"\<\!\-\- IF ([A-Za-z0-9\.\-_]+) \-\-\>", RegexOptions.Compiled);
/* if we have found an if on the line */
//if (rm.Success)
if (ioie >= 0)
{
string condition = line.Substring(ioi + 8, ioie - (ioi + 8));
if (inIf == 0 || inIf > 0 && conditionTrue.Peek())
{
inIf++;
string value1 = null;
//if (variables.ContainsKey(rm.Groups[1].Value))
//if (variables.TryGetValue(rm.Groups[1].Value, out value1))
// TODO: OR AND NOT
/*bool conditionFlag = false;
string[] conditionPhrases = condition.Split(new char[] { ' ' });
string lastOperator = null;
foreach (string phrase in condition)
{
value1 = null;
if (phrase == "OR" || phrase == "AND")
{
lastOperator = phrase;
}
else
{
variables.TryGetValue(condition, out value1);
bool flag = (value1.ToLower() != "false" && value1 != "0" && value1 != String.Empty);
switch (lastOperator)
{
case "OR":
conditionFlag = (conditionFlag || flag);
case "AND":
conditionFlag = (conditionFlag && flag);
default:
conditionFlag = flag;
}
}
}*/
if (variables.TryGetValue(condition, out value1))
{
//if (variables[rm.Groups[1].Value] != null)
if (value1 != null)
{
//if (variables[rm.Groups[1].Value].ToLower() != "false" && variables[rm.Groups[1].Value] != "0" && variables[rm.Groups[1].Value] != String.Empty)
if (value1.ToLower() != "false" && value1 != "0" && value1 != String.Empty)
{
conditionTrue.Push(true);
}
else
{
conditionTrue.Push(false);
rootFalse = inIf;
}
}
else
{
conditionTrue.Push(false);
rootFalse = inIf;
}
}
else
{
if (childIndex >= 0)
{
//string loopConditionVar = rm.Groups[1].Value;
string loopConditionVar = condition;
if (loopConditionVar.StartsWith(variables.Path + "."))
{
loopConditionVar = loopConditionVar.Substring(variables.Path.Length + 1);
}
string value2 = null;
if (variables.TryGetValue(loopConditionVar, out value2))
//if (variables.ContainsKey(loopConditionVar))
{
//if (variables[loopConditionVar] == null)
if (value2 == null)
{
conditionTrue.Push(false);
rootFalse = inIf;
}
else
{
//if (variables[loopConditionVar].ToLower() != "false" && variables[loopConditionVar] != "0" && variables[loopConditionVar] != String.Empty)
if (value2.ToLower() != "false" && value2 != "0" && value2 != String.Empty)
{
conditionTrue.Push(true);
}
else
{
conditionTrue.Push(false);
rootFalse = inIf;
}
}
}
else
{
conditionTrue.Push(false);
rootFalse = inIf;
}
}
else
{
conditionTrue.Push(false);
rootFalse = inIf;
}
}
}
else
{
inIf++;
conditionTrue.Push(false);
}
inElse.Push(false);
if (conditionTrue.Peek())
{
//line = line.Remove(rm.Index, rm.Length);
line = line.Remove(ioi, (ioie + 4) - ioi);
}
else
{
//line = line.Remove(rm.Index);
line = line.Remove(ioi);
}
}
}
// END IF
/*Match rmei = Regex.Match(line, @"\<\!\-\- ELSEIF ([A-Za-z0-9\.\-_]+) \-\-\>", RegexOptions.Compiled);
if (rmei.Success && inIf > 0)
{
if (conditionTrue.Count == inIf)
{
if (!conditionTrue.Peek())
{
if (rm.Success)
{
line = line.Remove(rm.Index, (rmei.Index - rm.Index + rmei.Length));
}
else
{
line = line.Remove(0, rmei.Index + rmei.Length);
}
}
/ * change the top most from false to true * /
inElse.Pop();
inElse.Push(true);
if (rootFalse == 0)
{
rootFalse = inIf;
}
else if (rootFalse == inIf)
{
rootFalse = 0;
}
}
}*/
int iol = line.IndexOf("");
if (iol >= 0 && inIf > 0)
{
//Match rme = Regex.Match(line, @"\<\!\-\- ELSE \-\-\>", RegexOptions.Compiled);
//if (rme.Success && inIf > 0)
{
if (conditionTrue.Count == inIf)
{
if (!conditionTrue.Peek())
{
//if (rm != null && rm.Success)
if (ioi >= 0)
{
//line = line.Remove(rm.Index, (rme.Index - rm.Index + rme.Length));
line = line.Remove(ioi, (iol - ioi + 13));
}
else
{
line = line.Remove(0, iol + 13);
}
}
/* change the top most from false to true */
inElse.Pop();
inElse.Push(true);
if (rootFalse == 0)
{
rootFalse = inIf;
}
else if (rootFalse == inIf)
{
rootFalse = 0;
}
}
}
}
int ion = line.IndexOf("");
if (ion >= 0 && inIf > 0)
{
//Match rmd = Regex.Match(line, @"\<\!\-\- ENDIF \-\-\>", RegexOptions.Compiled);
//if (rmd.Success && inIf > 0)
{
if (conditionTrue.Peek() & !inElse.Peek() || !conditionTrue.Peek() && inElse.Peek())
{
//line = line.Remove(rmd.Index, rmd.Length);
line = line.Remove(ion, 14);
}
else if (conditionTrue.Peek() & inElse.Peek() || !conditionTrue.Peek() && !inElse.Peek())
{
//line = line.Remove(0, rmd.Index + rmd.Length);
line = line.Remove(0, ion + 14);
}
if (rootFalse == inIf)
{
rootFalse = 0;
}
/* we have finished this level of 'if', we decrement our counter */
inIf--;
inElse.Pop();
conditionTrue.Pop();
}
}
if (conditionTrue.Count == inIf && inIf > 0)
{
if (!conditionTrue.Peek() && !inElse.Peek())
{
// don't append the line, stop processing the line
continue;
}
if (conditionTrue.Peek() && inElse.Peek())
{
// don't append the line, stop processing the line
continue;
}
if (inIf > rootFalse && rootFalse != 0)
{
continue;
}
}
/* Includes */
int iou;
//MatchCollection mc = Regex.Matches(line, @"\<\!\-\- INCLUDE ([A-Za-z0-9\.\-_]+) \-\-\>", RegexOptions.Compiled);
//foreach (Match ma in mc)
//for (int j = 0; j < mc.Count; j++)
while ((iou = line.IndexOf("", iou);
if (ioue >= 0)
{
string file = line.Substring(iou + 13, ioue - (iou + 13));
StringBuilder includeOutput = new StringBuilder();
//string[] includeLines = Template.OpenTextFile(Path.Combine(path, ma.Groups[1].Value)).Replace("\r", "").Split('\n');
//string[] includeLines = LoadTemplateFile(mc[j].Groups[1].Value).Replace("\r", String.Empty).Split('\n');
string[] includeLines = LoadTemplateFile(file).Replace("\r", String.Empty).Split('\n');
ProcessLines(includeLines, includeOutput, variables);
//line = line.Replace(string.Format("", mc[j].Groups[1].Value), includeOutput.ToString());
line = line.Replace(string.Format("", file), includeOutput.ToString());
}
}
/* experimental */
int offset = 0;
//MatchCollection varMatches = Regex.Matches(line, @"{([A-Z\-_]+)}", RegexOptions.Compiled);
//foreach (Match varMatch in varMatches)
List varMatches = GetVariablesFromLine(line);
foreach (TemplateVariable tv in varMatches)
{
//int nextOffset = -varMatch.Length;
//string key = varMatch.Groups[1].Value;
//line = line.Remove(varMatch.Index + offset, varMatch.Length);
string key = tv.Name;
int nextOffset = -(key.Length + 2);
line = line.Remove(tv.Index + offset, key.Length + 2);
string value4 = null;
//if (variables.ContainsKey(key))
if (variables.TryGetValue(key, out value4))
{
//if (variables[key] != null)
if (value4 != null)
{
//nextOffset += variables[key].Length;
//line = line.Insert(varMatch.Index + offset, variables[key]);
nextOffset += value4.Length;
line = line.Insert(tv.Index + offset, value4);
}
}
else if (prose != null && key.StartsWith("L_"))
{
string proseKey = key.Substring(2).ToUpper();
string fragment = null;
if (prose.ContainsKey(templateAssembly, proseKey))
{
fragment = prose.GetString(templateAssembly, proseKey);
}
else if (prose.ContainsKey(proseKey))
{
fragment = prose.GetString(proseKey);
}
else if ((!string.IsNullOrEmpty(templateAssembly)) && prose.ContainsKey(templateAssembly, proseKey))
{
fragment = prose.GetString(templateAssembly, proseKey);
}
if (fragment != null)
{
nextOffset += fragment.Length;
//line = line.Insert(varMatch.Index + offset, fragment);
line = line.Insert(tv.Index + offset, fragment);
}
}
offset += nextOffset;
}
/* loops/repetition blocks */
if (childIndex >= 0)
{
offset = 0;
//MatchCollection varMatches2 = Regex.Matches(line, string.Format(@"{{{0}\.([A-Z\-_]+)}}", Regex.Escape(variables.Path)), RegexOptions.Compiled);
List varMatches2 = GetLoopVariablesFromLine(variables.Path, line);
int varPathLength = variables.Path.Length;
//foreach (Match varMatch in varMatches2)
foreach (TemplateVariable tv in varMatches2)
{
/*int nextOffset = -varMatch.Length;
string key = varMatch.Groups[1].Value;
line = line.Remove(varMatch.Index + offset, varMatch.Length);*/
string key = tv.Name;
int nextOffset = -(key.Length + 3 + varPathLength);
line = line.Remove(tv.Index + offset, key.Length + 3 + varPathLength);
string value3 = null;
if (variables.TryGetValue(key, out value3))
//if (variables.ContainsKey(key))
{
if (value3 != null)
{
nextOffset += value3.Length;
line = line.Insert(tv.Index + offset, value3);
}
}
offset += nextOffset;
}
}
output.AppendLine(line);
}
}
public static TemplateVariable GetConstructFromLine(string constructType, string line)
{
return new TemplateVariable("HI", 0);
}
private static List GetVariablesFromLine(string line)
{
List mc = new List();
string varName = String.Empty;
bool inVar = false;
int varStart = 0;
for (int i = 0; i < line.Length; i++)
{
if (line[i] == '{')
{
inVar = true;
varName = String.Empty;
varStart = i;
continue;
}
if (inVar)
{
if (line[i] == '}')
{
mc.Add(new TemplateVariable(varName, varStart));
continue;
}
if ((line[i] >= 'A' && line[i] <= 'Z') || line[i] == '_' || line[i] == '-')
{
varName += line[i];
}
else
{
inVar = false;
continue;
}
}
}
return mc;
}
private static List GetLoopVariablesFromLine(string parent, string line)
{
List mc = new List();
string varName = String.Empty;
bool inVar = false;
int varStart = 0;
int parentIndex = 0;
int parentMaxIndex = parent.Length - 1;
for (int i = 0; i < line.Length; i++)
{
if (line[i] == '{')
{
inVar = true;
varName = String.Empty;
varStart = i;
parentIndex = 0;
continue;
}
if (inVar)
{
if (line[i] == '}')
{
mc.Add(new TemplateVariable(varName, varStart));
continue;
}
if (parentIndex < parent.Length)
{
if (line[i] == parent[parentIndex])
{
parentIndex++;
}
else
{
inVar = false;
continue;
}
}
else if (parentIndex == parent.Length)
{
if (line[i] == '.')
{
parentIndex++;
}
else
{
inVar = false;
continue;
}
}
else if ((line[i] >= 'A' && line[i] <= 'Z') || line[i] == '_' || line[i] == '-')
{
varName += line[i];
}
else
{
inVar = false;
continue;
}
}
}
return mc;
}
#region file handling
///
/// Open a text file
///
///
///
protected static string OpenTextFile(string fileName)
{
StreamReader myStreamReader;
string temp;
try
{
myStreamReader = File.OpenText(fileName);
temp = myStreamReader.ReadToEnd();
myStreamReader.Close();
}
catch
{
temp = String.Empty;
}
return temp;
}
protected static string OpenTextStream(Stream file)
{
StreamReader myStreamReader;
string temp;
try
{
myStreamReader = new StreamReader(file);
temp = myStreamReader.ReadToEnd();
myStreamReader.Close();
}
catch (Exception ex)
{
temp = "Error reading stream.
" + ex.ToString();
}
return temp;
}
///
/// Save a text file
///
///
///
protected static void SaveTextFile(string fileToSave, string fileName)
{
StreamWriter myStreamWriter = File.CreateText(fileName);
myStreamWriter.Write(fileToSave);
myStreamWriter.Close();
}
#endregion
}
}