// ******************************************************************************
// ******************************************************************************
// 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.Text.RegularExpressions;
using System.Diagnostics;
namespace AspNetDaST.Web.Private
{
internal class ScopeTreeModel
{
private TreeNodeScope RootScopeNode { get; set; }
internal Dictionary<PathExact, ScopeDataBag> ScopeDataMap { get; private set; }
internal List<PathExact> RefreshScopePaths { get; private set; }
internal RenderState State { get; set; }
internal ScopeTreeModel(ScopeController rootController)
{
ScopeDataMap = new Dictionary<PathExact, ScopeDataBag>();
RefreshScopePaths = new List<PathExact>();
State = RenderState.NotStarted;
// initial setup for ROOT node
RootScopeNode = new TreeNodeScope(this);
RootScopeNode.Controller = rootController;
RootScopeNode.IsControllerRoot = true;
rootController.TreeNode = RootScopeNode;
}
#region Model Tree Building
internal void RebuildControllerModel(ScopeController controller, PathExact renderPath)
{
// clear previously build model
controller.TreeNode.ResetModel();
// get controller template
controller.SetNavigator(renderPath, renderPath);
string template = controller.ProvideTemplate();
if (template == null) throw new DaSTException("Template was not set properly");
controller.SetNavigator(null, null);
// parse template and build controller subtree
TemplateTreeParser treeParser = new TemplateTreeParser();
treeParser.ExtendTreeModelFromNode(controller.TreeNode, template);
// set controller reference on all subtree nodes
SetSubtreeControllerRec(controller.TreeNode, controller);
// finally, do controller model initialization
controller.SetNavigator(renderPath, renderPath);
controller.InitializeModel(new ControllerModelBuilder(controller));
controller.SetNavigator(null, null);
}
internal void RebuildControllerModelsOnPath(PathExact path)
{
TreeNodeScope nextNode = RootScopeNode;
RebuildControllerModel(nextNode.Controller, PathExact.Empty);
for (int segmIdx = 0; segmIdx < path.Length; segmIdx++)
{
nextNode = nextNode.ScopeNodes.Find(node => node.ScopeID == path[segmIdx].Name);
if (nextNode == null) throw new DaSTException("Failed to rebuild controllers on path '{0}'", path);
if (nextNode.IsControllerRoot) RebuildControllerModel(nextNode.Controller, path.Subpath(segmIdx + 1));
}
}
private void SetSubtreeControllerRec(TreeNodeScope scopeNode, ScopeController controller)
{
foreach (var childNode in scopeNode.ScopeNodes)
{
childNode.Controller = controller;
SetSubtreeControllerRec(childNode, controller);
}
}
#endregion
#region Model Tree Functions
internal TreeNodeScope GetNode(PathBase path)
{
TreeNodeScope currNode = RootScopeNode;
for (int i = 0; i < path.Length && currNode != null; i++)
{
currNode = currNode.ScopeNodes.Find(node => node.ScopeID == path[i].Name);
}
if (currNode == null) throw new DaSTException("Scope '{0}' not found", path);
return currNode;
}
#endregion
#region Rendered Tree Functions
internal ScopeDataBag GetScopeDataBag(PathExact renderPath)
{
if (!ScopeDataMap.ContainsKey(renderPath)) ScopeDataMap.Add(renderPath, new ScopeDataBag(this, renderPath));
return ScopeDataMap[renderPath];
}
internal void InvokeDataBindingHandler(TreeNodeScope scopeNode, PathExact currentPath)
{
if (scopeNode.DataBindCallback != null)
{
// get path of the controller corresponding to the current node
var controlPath = currentPath.Subpath(scopeNode.Controller.TreeNode.NodePath.Length);
// set navigators to be used inside the handler to traverse model tree
scopeNode.Controller.SetNavigator(controlPath, currentPath);
// invoke databinding handler
scopeNode.DataBindCallback();
// unset navigators to prevent usage outside the handler
scopeNode.Controller.SetNavigator(null, null);
}
}
internal void InvokeScopeActionHandler(TreeNodeScope targetNode, PathExact actionPath, PathModel handlerPath, string actionName, object actionArg)
{
Debug.Assert(actionPath.Subpath(targetNode.NodePath.Length).IsSubpathOf(actionPath));
Debug.Assert(!string.IsNullOrEmpty(actionName));
// get action info using name and handler path
ModelActionInfo actionInfo = targetNode.ActionHandlers.Find(ai =>
ai.NodePath == handlerPath && ai.ActionName == actionName);
if (actionInfo != null)
{
// get exact path of the controller corresponding to the action node
var controlPath = actionPath.Subpath(targetNode.NodePath.Length);
// set navigators to be used inside the handler to traverse model tree
targetNode.Controller.SetNavigator(controlPath, actionPath);
// invoke action handler
actionInfo.Handler(actionArg);
// unset navigators to prevent usage outside the handler
targetNode.Controller.SetNavigator(null, null);
}
}
#endregion
#region Utility Classes
internal enum RenderState
{
NotStarted,
BuildingTree,
HandlingActions,
RenderingTree,
WritingOutput
}
#endregion
}
}