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

Introduction to Bellevue View Engine - Part 1

Rate me:
Please Sign up or sign in to vote.
3.43/5 (4 votes)
18 Mar 2010CPOL8 min read 26K   126   10  
Prototype of a new template engine for ASP.NET MVC Framework that respects HTML and uses CSS-like syntax for model binding.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;

namespace Ope.Bellevue.Rendering
{
    public class DataContext: IViewDataContainer 
    {
        ViewDataDictionary viewData;
        object itemData;
        int itemIndex = -1;

        public DataContext(ViewDataDictionary viewData)
        {
            this.viewData = viewData;
        }

        public DataContext GetItemContext(object itemdata, int itemIndex)
        {
            var itemContext = new DataContext(viewData);
            itemContext.itemData = itemdata;
            itemContext.itemIndex = itemIndex;
            return itemContext;
        }

        public bool GetBool(RenderingParameter param)
        {
            if (param == null)
            {
                return false;
            }
            object value = GetObject(param);
            if (value == null)
            {
                return false;
            }
            if (value is bool)
            {
                return (bool)value;
            }
            if (value is string && value.ToString().ToLower() == "false")
            {
                return false;
            }
            return true;
        }

        public string GetValue(RenderingParameter expression)
        {
            object result = GetObject(expression);
            if (result == null)
            {
                return "";
            }

            else
            {
                return result.ToString();
            }
        }

        public System.Collections.IEnumerable GetEnumerable(RenderingParameter expression)
        {
            object result = GetObject(expression);
            if (result == null)
            {
                return null;
            }
            if (!(result is string) && result is System.Collections.IEnumerable)
            {
                return (System.Collections.IEnumerable)result;
            }
            return new object[] { result };
        }

        public object GetObject(RenderingParameter parameter)
        {
            if (parameter == null)
            {
                return null;
            }
            if (parameter.Type == ParameterType.Function)
            {
                return getFunctionValue(parameter.Function);
            }
            else if (parameter.Type == ParameterType.Text)
            {
                return parameter.Value;
            }
            else
            {
                throw new NotSupportedException("Unknown parameter type: " + parameter.Type);
            }
        }

        private object getFunctionValue(FunctionParameter func)
        {
            string functionName = func.Name.ToLower();

            if (functionName == "concat")
            {
                StringBuilder sb = new StringBuilder();
                foreach (var param in func.Parameters)
                {
                    sb.Append(GetValue(param));
                }
                return sb.ToString();
            }
            if (functionName == "true")
            {
                return true;
            }
            if (functionName == "false")
            {
                return false;
            }
            if (functionName == "null")
            {
                return null;
            }

            // The rest of the functions use the first prameter separately, so fetching it here as object and string 
            object firstParamValue = func.Parameters.Count < 1 ? null : GetObject(func.Parameters[0]);
            string firstParamText = (firstParamValue ?? "").ToString();

            switch (functionName)
            {
                case "isnull":
                    if (firstParamValue == null)
                    {
                        return func.Parameters.Count > 1 ? GetObject(func.Parameters[1]) : null;
                    }
                    else
                    {
                        return firstParamValue;
                    }

                case "if":
                    if (func.Parameters.Count < 2)
                    {
                        return null;
                    }
                    bool condition = GetBool(func.Parameters[0]);
                    if (condition)
                    { 
                        return GetValue(func.Parameters[1]);
                    }
                    else
                    {
                        if (func.Parameters.Count == 2)
                        {
                            return null;
                        }
                        else
                        {
                            return GetValue(func.Parameters[2]);
                        }
                    }

                case "index":
                    if (itemIndex < 0)
                    {
                        return null;
                    }
                    int indexBase;
                    int.TryParse(firstParamText, out indexBase);
                    return itemIndex + indexBase;
                case "data":
                    if (firstParamText == "")
                    {
                        return viewData;
                    }
                    else
                    {
                        // TODO: Consider the performance of doing Eval directly vs. trying with the key first
                        return viewData.Eval(firstParamText);
                    }
                case "model":
                    if (firstParamText == "")
                    {
                        return viewData.Model;
                    }
                    else
                    {
                        // TODO: Consider restricting this to Model only (not ViewData dictionary)
                        return viewData.Eval(firstParamText);
                    }
                case "item":
                    if (firstParamText == "")
                    {
                        return itemData;
                    }
                    else
                    {
                        return getProperty(itemData, firstParamText);
                    }
            }

            // Collection functions (one parameter required to be IEnumerable)
            string[] collectionFunctions = new string[] { "join", "first", "last", "rest", "trunc", "strip", "length" };
            if (collectionFunctions.Contains(functionName))
            {
                var coll = firstParamValue as System.Collections.IEnumerable;
                if (coll == null)
                {
                    return null;
                }
                var objectColl = coll.Cast<object>();

                switch (functionName)
                {
                    case "join":
                        string separator = ", ";
                        if (func.Parameters.Count > 1)
                        {
                            separator = GetValue(func.Parameters[1]);
                        }
                        return string.Join(separator, objectColl.Select(o => (o ?? "").ToString()).ToArray());
                    case "first":
                        return objectColl.FirstOrDefault();
                    case "last":
                        return objectColl.LastOrDefault();
                    case "rest":
                        return objectColl.Skip(1);
                    case "trunc":
                        return objectColl.Take(objectColl.Count() - 1);
                    case "strip":
                        return from x in objectColl where x != null select x;
                    case "length":
                        return objectColl.Count();
                }
            }

            // Two parameters required
            if (functionName == "get")
            {
                if (func.Parameters.Count < 2)
                {
                    return null;
                }
                return getProperty(firstParamValue, GetValue(func.Parameters[1]));
            }

            throw new NotSupportedException("Function " + functionName + " not supported.");
        }

        private object getProperty(object container, string propertyExpression)
        {
            // TODO: This implementation is quite limited: should probably support indexers / dictionaries
            if (string.IsNullOrEmpty(propertyExpression) || container == null)
            {
                return null;
            }

            string[] propertyPath = propertyExpression.Split('.');
            object currentObj = container;
            for (int i = 0; i < propertyPath.Length; i++)
            {
                string propertyPart = propertyPath[i];
                System.ComponentModel.PropertyDescriptor descriptor
                    = System.ComponentModel.TypeDescriptor.GetProperties(currentObj).Find(propertyPart, true);
                if (descriptor == null)
                {
                    return null;
                }
                else
                {
                    currentObj = descriptor.GetValue(currentObj);
                    if (currentObj == null)
                    {
                        return null;
                    }
                }
            }
            return currentObj;
        }

        #region IViewDataContainer Members

        public ViewDataDictionary ViewData
        {
            get
            {
                return viewData; 
            }
            set
            {
                viewData = value;
            }
        }

        #endregion
    }
}

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
Founder OPE AG
Switzerland Switzerland
Olli is a .Net developer and architect.

He specializes in Asp.net MVC and other web technologies, XML and lately Domain Specific Languages.

Olli is originally from Finland, but currently works for his own one-man-initiative OPE AG (www.ope.ag) from Switzerland. He has over 10 years of experience as one of the founding partners and Chief Technology Officer of Quartal group of companies (www.quartal.com).

Comments and Discussions