Click here to Skip to main content
15,897,334 members
Articles / Web Development / ASP.NET

ASP.NET DaST to wrestle with MVC and WebForms

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
26 Mar 2013CPOL82 min read 23.5K   152   11  
DaST is a new architectural pattern for building highly dynamic Web 2.0 applications. A web page is rendered as a set of randomly nested rectangles where each rectangle is controlled individually and every combination of rectangles can be partially updated via AJAX.
// ******************************************************************************
// ******************************************************************************
// 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.Web.Script.Serialization;
using System.Collections.Specialized;

namespace AspNetDaST.Web.Private
{
  internal static class ScopeTreeTools
  {
    internal static class ClientBridge
    {
      public static string ConvertScopePath2ClientID(PathExact path)
      {
        StringBuilder sb = new StringBuilder("SCOPE");
        for (int i = 0; i < path.Length; i++) sb.AppendFormat("${0}-{1}", path[i].Axis ?? 0, path[i].Name);
        return sb.ToString();
      }

      public static PathExact ConvertClientID2ScopePath(string clientID)
      {
        PathExact path = new PathExact();

        Regex rePath = new Regex("^SCOPE(\\$(\\d+)\\-([A-Za-z0-9]+))+$");
        Match match = rePath.Match(clientID);

        if (match.Success)
        {
          for (int i = 0; i < match.Groups[1].Captures.Count; i++)
          {
            int axis = int.Parse(match.Groups[2].Captures[i].Value);
            string name = match.Groups[3].Captures[i].Value;
            path = path.Append(axis, name);
          }
        }
        else throw new DaSTException("Invalid client scope ID format");

        return path;
      }

      public static string[] DecodeClientData(string data)
      {
        try
        {
          List<string> values = new List<string>();
          int startIdx = 0, delimIdx;

          while (startIdx < data.Length)
          {      
            if ((delimIdx = data.IndexOf('|', startIdx)) == -1) throw new Exception("Expected delimiter");
            int dataLen = int.Parse(data.Substring(startIdx, delimIdx - startIdx));
            startIdx = delimIdx + 1;

            string dataValue = data.Substring(startIdx, dataLen);
            startIdx = startIdx + dataLen;

            if (data[startIdx] != '|') throw new Exception("Expected delimiter");
            startIdx++;

            values.Add(dataValue);
          }

          return values.ToArray();
        }
        catch (Exception ex) { throw new Exception("Failed to decode client data"); }
      }
    }

    internal static class DaSTState
    {
      private static JavaScriptSerializer _json = new JavaScriptSerializer();

      public static string BuildDaSTState(ScopeTreeModel scopeTree)
      {
        // collect scope datas to store in state
        var scopeDatas = from path in scopeTree.ScopeDataMap.Keys
                         let data = scopeTree.ScopeDataMap[path]
                         // select only needed datas according to some rules:
                         where data.Validated &&                      // 1) data must be validated 
                         data.RenderType != ScopeRenderType.None &&   // 2) must be visible
                         data.StoredParams.Count > 0 &&               // 3) must have storable params 
                         !scopeTree.ScopeDataMap.Any(kv =>            // 4) must not be on a subtree of empty of invisible scope    
                           kv.Key.IsSubpathOf(path) && kv.Key != path &&
                           (kv.Value.RenderType == ScopeRenderType.None ||
                           kv.Value.RenderType == ScopeRenderType.Empty))
                         select data;
     
        // finally, serialize scopes and encode the result to put on the page  
        string serializedData = SerializeScopeDatas(scopeDatas);
        return Convert.ToBase64String(Encoding.ASCII.GetBytes(serializedData));
      }

      public static void RestoreDaSTState(string state, ScopeTreeModel scopeTree)
      {
        string serializedData = Encoding.ASCII.GetString(Convert.FromBase64String(state));

        var dataByClientID = ParseSerializedState(serializedData);

        foreach (var kv in dataByClientID)
        {
          PathExact renderPath = ClientBridge.ConvertClientID2ScopePath(kv.Key);
          var dict = _json.Deserialize<Dictionary<string, object>>(kv.Value);

          ScopeDataBag scopeData = new ScopeDataBag(scopeTree, renderPath);
          // populate stored params with values that we just deserialized
          foreach (var pair in dict) scopeData.StoredParams.Add(pair.Key, pair.Value);
          // set flag on restored scope to indicate that this scope is valid for following serialization
          scopeData.Validated = true;

          scopeTree.ScopeDataMap.Add(renderPath, scopeData);
        }
      }
    
      private static string SerializeScopeDatas(IEnumerable<ScopeDataBag> scopeDatas)
      {
        StringBuilder sb = new StringBuilder();

        foreach (var scopeData in scopeDatas)
        {
          string strParams = _json.Serialize(scopeData.StoredParams);
          sb.AppendFormat("{0}|{1}|{2}|", strParams.Length, scopeData.ScopeClientID, strParams); 
        }

        return sb.ToString();
      }

      private static List<KeyValuePair<string, string>> ParseSerializedState(string serializedData)
      {
        try
        {
          var results = new List<KeyValuePair<string, string>>();

          int startIdx = 0, delimIdx;
          while (startIdx < serializedData.Length)
          {
            if ((delimIdx = serializedData.IndexOf('|', startIdx)) == -1) throw new Exception("Expected delimiter");
            int dataLen = int.Parse(serializedData.Substring(startIdx, delimIdx - startIdx));
            startIdx = delimIdx + 1;

            if ((delimIdx = serializedData.IndexOf('|', startIdx)) == -1) throw new Exception("Expected delimiter");
            string clientID = serializedData.Substring(startIdx, delimIdx - startIdx);
            startIdx = delimIdx + 1;

            string data = serializedData.Substring(startIdx, dataLen);
            startIdx += dataLen;

            if (serializedData[startIdx] != '|') throw new Exception("Expected delimiter");
            startIdx++;

            results.Add(new KeyValuePair<string, string>(clientID, data));
          }

          return results;
        }
        catch (Exception ex) { throw new Exception("Failed to parse serialized state"); }
      }
    }

    internal static class Model
    {
      internal static bool IsScopeAccessible(ScopeController controller, TreeNodeScope scopeNode)
      {
        // scope is accessible if corresponding node is within current controller
        return object.Equals(controller, scopeNode.Controller) ||                                         // same controller
          (scopeNode.ParentNode != null && object.Equals(controller, scopeNode.ParentNode.Controller));   // leaf node with assigned controller
      }
    }
  }
}

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Canada Canada
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.

Comments and Discussions