// ******************************************************************************
// ******************************************************************************
// 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 System.IO;
namespace AspNetDaST.Web.Private
{
internal class ScopeTreeRenderer
{
private ScopeTreeModel _scopeTree;
private Stream _output;
internal ScopeTreeRenderer(ScopeTreeModel scopeTree)
{
_scopeTree = scopeTree;
_output = null;
}
public void RenderSubtree(PathExact renderPath, Stream outputStream)
{
_output = outputStream;
var startNode = _scopeTree.GetNode(renderPath);
RenderSubtreeRec(renderPath, startNode, true);
}
private void RenderSubtreeRec(PathExact renderPath, TreeNodeScope scopeNode, bool bNeedContainer)
{
// if current node is a controller, then rebuild the controller model
if (scopeNode.IsControllerRoot)
{
_scopeTree.RebuildControllerModel(scopeNode.Controller, renderPath);
}
// get scope data for the current rendering path
var scopeData = _scopeTree.GetScopeDataBag(renderPath);
// set flag to indicate that current scope instance is
// processed by the renderer and needs to be serialized
scopeData.Validated = true;
//
// Scope data binding
//
// do databinding only if current scope is visible
if (scopeData.RenderType == ScopeRenderType.Normal)
{
_scopeTree.InvokeDataBindingHandler(scopeNode, renderPath);
}
//
// Render scope contents
//
// visible scope outputs all its content
if (scopeData.RenderType == ScopeRenderType.Normal)
{
if (!bNeedContainer) RenderContainerStartTag(scopeNode, scopeData);
// TODO: revisit how FromNodeIdx and StopNodeIdx values are set
// render if it's not an empty scope
if (scopeNode.Nodes.Count > 0)
{
// TODO: make sure that there are only HTML nodes until limiter
// output raw text before scope-from limiter
for (int i = 0; i < scopeNode.FromNodeIdx; i++)
{
var childHtmlNode = (TreeNodeHtml)scopeNode.Nodes[i];
RenderRawText(childHtmlNode.HtmlText.ToString());
}
// rendered content between scope-from and scope-stop on each axis
foreach (var axis in scopeData.DataBinding.AxisBindings)
{
for (int nodeIdx = scopeNode.FromNodeIdx; nodeIdx <= scopeNode.StopNodeIdx; nodeIdx++)
{
// on child scope call the rendering function recursively
if (scopeNode.Nodes[nodeIdx] is TreeNodeScope)
{
var childScopeNode = (TreeNodeScope)scopeNode.Nodes[nodeIdx];
var childPath = renderPath.Append(axis.AxisNum, childScopeNode.ScopeID);
RenderSubtreeRec(childPath, childScopeNode, false);
}
// otherwise, child node is an html template fragment that has to be rendered
else
{
var childHtmlNode = (TreeNodeHtml)scopeNode.Nodes[nodeIdx];
RenderTemplateFragment(childHtmlNode.HtmlText.ToString(), axis.Renderers, scopeData);
}
}
}
// output raw text after scope-stop limiter
for (int i = scopeNode.StopNodeIdx + 1; i < scopeNode.Nodes.Count; i++)
{
var childHtmlNode = (TreeNodeHtml)scopeNode.Nodes[i];
RenderRawText(childHtmlNode.HtmlText.ToString());
}
}
if (!bNeedContainer) RenderContainerEndTag(scopeNode);
}
// empty scope output is only a container
else if (scopeData.RenderType == ScopeRenderType.Empty)
{
// render scope container only
if (!bNeedContainer)
{
RenderContainerStartTag(scopeNode, scopeData);
RenderContainerEndTag(scopeNode);
}
}
// hidden scope does not output anything
else if (scopeData.RenderType == ScopeRenderType.None)
{
// dont output anything
}
}
private void RenderRawText(string text)
{
byte[] bytes = Encoding.ASCII.GetBytes(text);
_output.Write(bytes, 0, bytes.Length);
}
private void RenderTemplateFragment(string template, List<HtmlContentRenderer> rendererSet, ScopeDataBag scopeData)
{
// create new list of renderers and add some predefined replacements to it
var renderers = new List<HtmlContentRenderer>();
if (rendererSet != null) renderers.AddRange(rendererSet);
// add some system replacements
renderers.Add(new HtmlContentRenderer_SingleReplacement("{scope:ClientID}", scopeData.ScopeClientID));
// apply sequence of renderers
foreach (var renderer in renderers)
{
template = renderer.RenderContent(template);
}
RenderRawText(template);
}
private void RenderContainerStartTag(TreeNodeScope scopeNode, ScopeDataBag scopeData)
{
// take only attributes without 'dast:' prefix
var attrs = from attrName in scopeNode.TagAttributes.Keys.Cast<string>()
where !attrName.StartsWith("dast:", StringComparison.OrdinalIgnoreCase)
select new { Name = attrName, Value = scopeNode.TagAttributes[attrName] };
StringBuilder sb = new StringBuilder();
foreach (var attr in attrs) sb.AppendFormat(" {0}=\"{1}\"", attr.Name, attr.Value);
// construct final tag
string strTag = string.Format("<{0} id=\"{1}\"{2}>",
scopeNode.TagOriginalName.ToLower(),
scopeData.ScopeClientID,
sb.ToString());
RenderRawText(strTag);
}
private void RenderContainerEndTag(TreeNodeScope scopeNode)
{
string strTag = string.Format("</{0}>", scopeNode.TagOriginalName.ToLower());
RenderRawText(strTag);
}
}
}