|
// ******************************************************************************
// ******************************************************************************
// ASP.NET DaST Rendering Engine
// Copyright (C) 2011 Roman Gubarenko
// Project Home: http://aspnetdast.sourceforge.net/
// ******************************************************************************
// ******************************************************************************
//
// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
//
// ******************************************************************************
// Primary Contact: rgubarenko@gmail.com
// Learn More: http://aspnetdast.sourceforge.net/
// Project Repository: http://sourceforge.net/projects/aspnetdast/
// ******************************************************************************
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using AspNetDaST.HtmlParsing;
namespace AspNetDaST.Web.Private
{
internal class TemplateTreeParser
{
private readonly string[] ValidScopeTags = new string[] { "div", "span", "td" };
private Stack<KeyValuePair<string, string>> m_stackTags;
private TreeNodeScope m_currNode;
private HtmlParser m_parser;
public TemplateTreeParser()
{
m_stackTags = new Stack<KeyValuePair<string, string>>();
m_currNode = null;
}
public void ExtendTreeModelFromNode(TreeNodeScope startNode, string template)
{
try
{
m_currNode = startNode;
m_parser = new HtmlParser(template);
while (m_parser.ReadNextNode())
{
// opening element allowed to be scope container (i.e. DIV, SPAN, etc.)
if (m_parser.NodeType == NodeTypes.Element && AllowedScopeTag(m_parser.Name))
{
string scopeID = m_parser.Attributes["dast:scope"];
if (scopeID != null)
{
PushStack(m_parser.Name, scopeID);
m_currNode = CreateChildScopeNode(scopeID);
if (m_parser.IsEmptyElement)
{
PopStack();
CommitScopeNode(m_currNode);
m_currNode = m_currNode.ParentNode;
}
continue;
}
else
{
PushStack(m_parser.Name, null);
if (m_parser.IsEmptyElement) PopStack();
}
}
// closing element allowed to be scope container (i.e. DIV, SPAN, etc.)
else if (m_parser.NodeType == NodeTypes.EndElement && AllowedScopeTag(m_parser.Name))
{
// pop recent allowed tag from stack
var tag = PopStack();
string stackTagName = tag.Key;
string stackScopeID = tag.Value;
// ensure well formness of allowed tags
if (!string.Equals(m_parser.Name, stackTagName, StringComparison.OrdinalIgnoreCase))
throw new DaSTException("Tags in the template are not well formed");
// if current tag closes scope container, then
// we finalize current scope and jump to next one
if (stackScopeID != null)
{
// also make sure closing tag name matches tag name of current scope
if (!string.Equals(m_currNode.TagOriginalName, stackTagName, StringComparison.OrdinalIgnoreCase))
throw new DaSTException("Scopes in the template are not well formed");
CommitScopeNode(m_currNode);
m_currNode = m_currNode.ParentNode;
continue;
}
}
// comment element
else if (m_parser.NodeType == NodeTypes.Comment)
{
if (m_parser.Value == "scope-from")
{
MarkScopeFromNode();
continue;
}
else if (m_parser.Value == "scope-stop")
{
MarkScopeStopNode();
continue;
}
}
CreateChildHtmlNode();
}
// if everything is OK, currScope must be an initial scope
// and stack must be empty to ensure well formed divs structure
if (!object.Equals(m_currNode, startNode) || m_stackTags.Count != 0)
throw new DaSTException("Scope tags in the template are not closed properly");
// finalize initial node
CommitScopeNode(m_currNode);
}
catch (DaSTException ex) { throw new DaSTException(ex, "Failed to parse template"); }
}
private TreeNodeScope CreateChildScopeNode(string scopeID)
{
// TODO: ensure there is no ID attribute
TreeNodeScope newNode = new TreeNodeScope(m_currNode.ScopeTree);
newNode.ScopeID = scopeID;
newNode.NodePath = m_currNode.NodePath.Append(scopeID);
newNode.ParentNode = m_currNode;
newNode.TagOriginalName = m_parser.Name;
newNode.TagAttributes.Add(m_parser.Attributes);
m_currNode.Nodes.Add(newNode);
return newNode;
}
private TreeNodeHtml CreateChildHtmlNode()
{
TreeNodeHtml newNode = m_currNode.Nodes.LastOrDefault() as TreeNodeHtml;
if (newNode == null ||
m_currNode.Nodes.Count == m_currNode.FromNodeIdx ||
m_currNode.Nodes.Count - 1 == m_currNode.StopNodeIdx)
{
newNode = new TreeNodeHtml();
newNode.ParentNode = m_currNode;
m_currNode.Nodes.Add(newNode);
}
newNode.HtmlText.Append(m_parser.NodeHtml);
return newNode;
}
private void MarkScopeFromNode()
{
m_currNode.FromNodeIdx = m_currNode.Nodes.Count;
}
private void MarkScopeStopNode()
{
m_currNode.StopNodeIdx = m_currNode.Nodes.Count - 1;
}
private void CommitScopeNode(TreeNodeScope scopeNode)
{
if (scopeNode.Nodes.Count > 0)
{
if (scopeNode.FromNodeIdx < 0 && scopeNode.StopNodeIdx < 0)
{
scopeNode.FromNodeIdx = 0;
scopeNode.StopNodeIdx = Math.Max(scopeNode.Nodes.Count - 1, 0);
}
// validate scope fragments from/stop configuration
if (scopeNode.FromNodeIdx < 0 ||
scopeNode.FromNodeIdx > scopeNode.StopNodeIdx ||
scopeNode.StopNodeIdx > scopeNode.Nodes.Count - 1)
{
throw new DaSTException("Invalid scope from/stop: {0}", scopeNode.NodePath);
}
}
else
{
scopeNode.FromNodeIdx = -1;
scopeNode.StopNodeIdx = -1;
}
}
private bool AllowedScopeTag(string tagName)
{
return ValidScopeTags.Contains(tagName);
}
private void PushStack(string tagName, string scopeID)
{
m_stackTags.Push(new KeyValuePair<string, string>(tagName, scopeID));
}
private KeyValuePair<string, string> PopStack()
{
return m_stackTags.Pop();
}
}
}
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
Software Architect with over 15 years in IT field. Started with deep math and C++ Computer Vision software. Currently in .NET and PHP web development. Creator of DaST pattern, open-source frameworks, and plugins. Interested in cutting Edge IT, open-source, Web 2.0, .NET, MVC, C++, Java, jQuery, Mobile tech, and extreme sports.