Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
tree: 611b10a3d0
Fetching contributors…

Cannot retrieve contributors at this time

511 lines (467 sloc) 13.241 kb
/*
* TemplateEngine for .NET
* Copyright (C) 2003 Niels Wojciech Tadeusz Andersen <haj@zhat.dk>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
using System.Reflection;
using System;
using System.IO;
using System.Text;
public class Template
{
/*
* Template field and section definitions
*/
private const string DELIMITER = "@@";
private const string SECTIONTAG_HEAD = "<!-- @@";
private const string SECTIONTAG_TAIL = "@@ -->";
private const int DELIMITER_LEN = 2;
private const int SECTIONTAG_HEAD_LEN = 7;
private const int SECTIONTAG_TAIL_LEN = 6;
// Hash table size.
// Number of fields or sections will probably be less than 10
private const int MAX_FIELDS = 31;
// Create an empty initialized template
public Template()
{
head = new node();
addedHead = new node();
addedTail = addedHead;
firstSection = new section();
tpl = this;
fields = new Object[MAX_FIELDS];
sections = new Object[MAX_FIELDS];
}
// Create using template file
public Template(string filename)
{
head = new node();
addedHead = new node();
addedTail = addedHead;
firstSection = new section();
tpl = this;
fields = new Object[MAX_FIELDS];
sections = new Object[MAX_FIELDS];
StreamReader re = new StreamReader(filename, System.Text.Encoding.Default);
string data = SECTIONTAG_HEAD+re.ReadToEnd()+SECTIONTAG_TAIL;
re.Close();
construct(data, SECTIONTAG_HEAD_LEN, data.Length-SECTIONTAG_TAIL_LEN);
}
// Copy ctor
public Template(Template srctpl)
{
tpl = this;
fields = new Object[MAX_FIELDS];
sections = new Object[MAX_FIELDS];
head = new node();
addedHead = new node();
addedTail = addedHead;
firstSection = new section();
node tail = head;
node currNode = srctpl.head;
section lastSection = firstSection;
section currSection = srctpl.firstSection.nextSection;
for (; currSection != null; currSection = currSection.nextSection)
{
// Copy part before section
while (currNode != currSection.preceding)
{
currNode = currNode.next;
tail.next = new node();
tail = tail.next;
if (currNode.val.shared)
tail.val = _produceField(((field)currNode.val).key);
else
tail.val = new cell();
tail.val.val = currNode.val.val;
}
// Create section entry
lastSection.nextSection = _produceSection(currSection.key);
lastSection = lastSection.nextSection;
lastSection.preceding = tail;
lastSection.tpl = new Template(currSection.tpl);
lastSection.tpl.parent = this;
// Copy added content
if (currSection.tpl.addedHead.next != null)
{
int len = 0;
for (node n=currSection.tpl.addedHead.next; n!=null; n=n.next)
len += n.val.val.Length;
lastSection.tpl.addedTail.next = new node();
lastSection.tpl.addedTail = lastSection.tpl.addedTail.next;
lastSection.tpl.addedTail.val = new cell();
// Make content string
StringBuilder sb = new StringBuilder(len);
for (node n=currSection.tpl.addedHead.next; n!=null; n=n.next)
sb.Append(n.val.val);
lastSection.tpl.addedTail.val.val = sb.ToString();
}
}
// Copy rest
while ((currNode = currNode.next) != null)
{
tail.next = new node();
tail = tail.next;
if (currNode.val.shared)
tail.val = _produceField(((field)currNode.val).key);
else
tail.val = new cell();
tail.val.val = currNode.val.val;
}
}
// This is private method, but it shows how the thing gets created using recursion
// so I let it stay close to top.
private void construct(string data, int oLast, int oEnd)
{
node tail = head;
section lastSection = firstSection;
int oCurr, oNext;
while ((oCurr = data.IndexOf(DELIMITER, oLast)) != -1 && oCurr < oEnd)
{
// Move past delimiter
oCurr += DELIMITER_LEN;
// Find matching delimiter
if ((oNext = data.IndexOf(DELIMITER, oCurr)) == -1 || oNext > oEnd)
fail("bad syntax: missing delimiter");
// Section
if (String.Compare(SECTIONTAG_HEAD, 0, data, oCurr-SECTIONTAG_HEAD_LEN, SECTIONTAG_HEAD_LEN) == 0
&& String.Compare(SECTIONTAG_TAIL, 0, data, oNext, SECTIONTAG_TAIL_LEN) == 0)
{
// If there is any text since last field or section tag
if (oCurr - oLast > SECTIONTAG_HEAD_LEN)
{
tail.next = new node();
tail = tail.next;
tail.val = new cell();
tail.val.val = data.Substring(oLast, oCurr-SECTIONTAG_HEAD_LEN-oLast);
}
string tag = data.Substring(oCurr-SECTIONTAG_HEAD_LEN,oNext-oCurr+SECTIONTAG_HEAD_LEN+SECTIONTAG_TAIL_LEN);
section s = _produceSection(data.Substring(oCurr,oNext-oCurr));
oLast = oNext + SECTIONTAG_TAIL_LEN;
oNext = data.IndexOf(tag, oLast);
if (oNext == -1 || oNext > oEnd)
fail ("Bad syntax: Missing section end tag");
if (s.preceding != null)
fail ("Bad syntax: Duplicate section");
s.preceding = tail;
s.tpl = new Template();
s.tpl.construct(data, oLast, oNext);
s.tpl.parent = this;
lastSection.nextSection = s;
lastSection = s;
oLast = oNext + tag.Length;
}
// Field
else
{
if (oCurr - oLast > DELIMITER_LEN)
{
tail.next = new node();
tail = tail.next;
tail.val = new cell();
tail.val.val = data.Substring(oLast, oCurr-DELIMITER_LEN-oLast);
}
field f = _produceField(data.Substring(oCurr,oNext - oCurr));
tail.next = new node();
tail = tail.next;
tail.val = f;
oLast = oNext + DELIMITER_LEN;
}
}
if (oLast < oEnd)
{
// Save rest of text
tail.next = new node();
tail.next.val = new cell();
tail.next.val.val = data.Substring(oLast, oEnd-oLast);
}
}
public void reset()
{
for (int i=0; i<MAX_FIELDS; i++)
{
for (field f = (field)fields[i]; f != null; f = f.next)
f.val = "";
}
for (section s = firstSection.nextSection; s != null; s = s.nextSection)
{
s.tpl.addedHead.next = null;
s.tpl.addedTail = s.tpl.addedHead;
s.tpl.reset();
}
}
public void setField(string key, string val)
{
field f = tpl._getField(key);
if (f != null)
f.val = val;
}
public void setField(string key, int val)
{
field f = tpl._getField(key);
if (f != null)
f.val = val+"";
}
public void setField(string key, double val)
{
field f = tpl._getField(key);
if (f != null)
f.val = val+"";
}
public void setFieldGlobal(string key, string val)
{
setField(key, val);
for (section s=firstSection.nextSection; s!=null; s=s.nextSection)
s.tpl.setFieldGlobal(key, val);
}
public void setFieldGlobal(string key, int val)
{
setFieldGlobal(key, val+"");
}
public void setFieldGlobal(string key, double val)
{
setFieldGlobal(key, val+"");
}
public void setFieldFromFile(string key, string filename)
{
field f = _getField(key);
if (f != null)
{
StreamReader re = File.OpenText(filename);
f.val = re.ReadToEnd();
re.Close();
}
}
public void selectSection(string key)
{
section s = tpl._getSection(key);
if (s == null)
fail ("selectSection: Cannot find section "+key);
tpl.tpl = s.tpl;
tpl = tpl.tpl;
}
public void deselectSection()
{
if (tpl == this)
fail ("deselectSection: No section selected");
tpl = tpl.parent;
}
// This is definitely the part that beats C code in speed.
// Allocating memory is very fast in .NET
public void appendSection()
{
if (tpl == this)
fail ("appendSection: No section selected");
node currNode = tpl.head;
for (section currSection=tpl.firstSection.nextSection; currSection!=null; currSection=currSection.nextSection)
{
while (currNode != currSection.preceding)
{
currNode = currNode.next;
tpl.addedTail.next = new node();
tpl.addedTail = tpl.addedTail.next;
tpl.addedTail.val = new cell();
tpl.addedTail.val.val = currNode.val.val;
}
if (currSection.tpl.addedHead.next != null)
{
tpl.addedTail.next = currSection.tpl.addedHead.next;
tpl.addedTail = currSection.tpl.addedTail;
currSection.tpl.addedHead.next = null;
currSection.tpl.addedTail = currSection.tpl.addedHead;
}
}
while ((currNode = currNode.next) != null)
{
tpl.addedTail.next = new node();
tpl.addedTail = tpl.addedTail.next;
tpl.addedTail.val = new cell();
tpl.addedTail.val.val = currNode.val.val;
}
}
public void attachSection(string key)
{
selectSection(key);
appendSection();
deselectSection();
}
public void setSection(string key, string data)
{
section s = tpl._getSection(key);
if (s == null)
fail("setSection: Cannot find section "+key);
node n = new node();
n.val = new cell();
n.val.val = data;
n.next = s.preceding.next;
s.preceding.next = n;
s.preceding = n;
}
public void setSectionFromFile(string key, string filename)
{
section s = tpl._getSection(key);
if (s == null)
fail("setSection: Cannot find section "+key);
node n = new node();
n.val = new cell();
StreamReader re = File.OpenText(filename);
n.val.val = re.ReadToEnd();
re.Close();
n.next = s.preceding.next;
s.preceding.next = n;
s.preceding = n;
}
public string getSection(string key)
{
section s = tpl._getSection(key);
if (s == null)
fail("getSection: Cannot find section "+key);
return s.tpl.getContent();
}
public string getContent()
{
int len = 0;
// Traverse this' content to get length
for (node n=head.next; n!=null; n=n.next)
len += n.val.val.Length;
// Traverse all sections to add length of content added to them
for (section currSection=firstSection.nextSection; currSection!=null; currSection=currSection.nextSection)
{
for (node n=currSection.tpl.addedHead.next; n!=null; n=n.next)
len += n.val.val.Length;
}
// Traverse to get content in the right order
node currNode = head;
StringBuilder sb = new StringBuilder(len);
for (section currSection=firstSection.nextSection; currSection!=null; currSection=currSection.nextSection)
{
while (currNode != currSection.preceding)
{
currNode = currNode.next;
sb.Append(currNode.val.val);
}
for (node n=currSection.tpl.addedHead.next; n!=null; n=n.next)
sb.Append(n.val.val);
}
while ((currNode = currNode.next) != null)
sb.Append(currNode.val.val);
return sb.ToString();
}
/*
* Private methods
*/
private field _getField(string key)
{
field f = (field)fields[(uint)(key.GetHashCode()) % MAX_FIELDS];
while (f != null)
{
if (f.key == key)
return f;
f = f.next;
}
return null;
}
private field _produceField(string key)
{
uint pos = (uint)(key.GetHashCode()) % MAX_FIELDS;
field f = (field)fields[pos];
while (f != null)
{
if (f.key == key)
return f;
f = f.next;
}
f = new field(key);
f.next = (field)fields[pos];
fields[pos] = f;
return f;
}
private section _getSection(string key)
{
section s = (section)sections[(uint)(key.GetHashCode()) % MAX_FIELDS];
while (s != null)
{
if (s.key == key)
return s;
s = s.next;
}
return null;
}
private section _produceSection(string key)
{
uint pos = (uint)(key.GetHashCode()) % MAX_FIELDS;
section s = (section)sections[pos];
while (s != null)
{
if (s.key == key)
return s;
s = s.next;
}
s = new section();
s.key = key;
s.next = (section)sections[pos];
sections[pos] = s;
return s;
}
private void fail(string msg)
{
throw(new Exception("TemplateEngine.Template::"+msg));
}
private class section
{
public Template tpl;
public string key;
public node preceding;
public section next;
public section nextSection;
}
/*
* Internal data types
*/
private class node
{
public cell val;
public node next;
}
private class cell
{
public cell()
{
shared = false;
}
public string val;
public bool shared;
}
private class field : cell
{
public field(string Key)
{
key = Key;
val = "";
shared = true;
}
public string key;
public field next;
}
private node head;
private node addedHead;
private node addedTail;
private Object[] fields;
private Object[] sections;
private section firstSection;
private Template tpl;
private Template parent;
}
Jump to Line
Something went wrong with that request. Please try again.