zoukankan      html  css  js  c++  java
  • 模版引擎之简单实现

    模版,大家肯定都比较熟悉的一个概念,刚学C#(Java)那会老师就告诉我们,类是对象的模版。今天写这个模版其实是我用于生成js代码的,当然不限于生成js,其实跟codesmith有着差不多的功能,只是没那么强大,下面我写一些思路

    我这个模版目前用于生成Ext的Grid和添加,编辑 表单,所以主角的又(怎么会是又呢,呵呵)是数据库表,不过这些信息被我存到前一篇博客里提到的Model里去了(通过特性的方式)。

            出于方便,我还是把Model的代码贴一下: 

    Model
    //------------------------------------------------------------------------------
    // <auto-generated>
    //     This code generated by the tool, do not propose to amend
    //       Generation time:2012/7/16 18:01:54
    // </auto-generated>
    //------------------------------------------------------------------------------
    using System;
    using System.Data;
    using System.Runtime.Serialization;
    using XDbFramework;
    using System.Xml.Serialization;
    using System.Diagnostics;
    using System.CodeDom.Compiler;

    namespace ExinSoft.Host.Model
    {
        [Serializable]
        [Table(TableName = "Agent" ,Descripton = "代理")]
          [GeneratedCodeAttribute("System.Xml""2.0.50727.4927")]
        [DebuggerStepThroughAttribute()]
        [XmlRootAttribute(Namespace = "http://www.scexin.com/", IsNullable = true)]
        [DataContract(Namespace = "http://www.scexin.com/")]
        public partial class Model_Agent
        {
            
            [Column(KeyType = KeyTypeEnum.PrimaryKey,ColumnName="AgentID",DbType=SqlDbType.BigInt, Index=0,Description="编号")]
            [DataMember(Order = 0)]
            public  long? AgentID{get;set;}
        
            
            [Column(ColumnName="AgentLevelID",ForeignKeyTableName="AgentLevel",ForeignKeyFiledName="AgentLevelID", DbType=SqlDbType.Int, Index=1,Description="等级编号")]
            [DataMember(Order = 1)]
            public  int? AgentLevelID{get;set;}
        
            
            [Column(ColumnName="SuperiorAgentID",ForeignKeyTableName="Agent",ForeignKeyFiledName="AgentID", DbType=SqlDbType.BigInt, Index=2,Description="套餐编号")]
            [DataMember(Order = 2)]
            public  long? SuperiorAgentID{get;set;}
        
            
            [Column(ColumnName="CustomerID",ForeignKeyTableName="Customers",ForeignKeyFiledName="CustomerID", DbType=SqlDbType.BigInt, Index=3,Description="客户编号")]
            [DataMember(Order = 3)]
            public  long? CustomerID{get;set;}
        
            
            [Column(ColumnName="AddTime",DbType=SqlDbType.DateTime, Index=4,Description="操作时间")]
            [DataMember(Order = 4)]
            public  DateTime? AddTime{get;set;}
        
        }    
    }

    没错,这些信息被存放于储如“[Column(ColumnName="AgentLevelID",ForeignKeyTableName="AgentLevel",ForeignKeyFiledName="AgentLevelID", DbType=SqlDbType.Int, Index=1,Description="等级编号")]”这行特性标记中,稍作解释

    ColumnName :列名,ForeignKeyTableName:外键表名,ForeignKeyFiledName:外键表的主键列名,DbType:字段类型,Description:描述

     有了这些基本信息,便够了,现在我们就来解释上一篇讲到的数据库表数据的浏览功能,主角(模版)登场 ,分步实现

    1.定义模块标签,现在模版功能还不是很强大,没有实现语法分析如if等,目前只是简单的实现了for和标签替换,上代码 

    模块标签定义
    public const string ColumnEachTagName = "columnsEach"//循环列
            public const string DateColumnTagName = "dateColumn";  //日期列
            public const string PkColumnTagName = "pkColumn";      //主键列
            public const string TextColumnTagName = "textColumn";  //文本列
            public const string BoolColumnTagName = "boolColumn";  //bool列
            public const string EveryColumnTagName = "everyColumn";//所有列
            public const string NumberColumnTagName = "numberColumn";//数字列
            public const string MoneyColumnTagName = "moneyColumn";  //money型列
            public const string ColumnNameTagName = "[columnName]";  //列名
            public const string UnlessLastTagName = "unlessLast";    //除非最后一个
            public const string TableTagName = "[table]";            //表名
            public const string FkColumnTagName = "fkColumn";        //外键列名
            public const string FkTableTagName = "[fkTable]";        //外键表名
            public const string ColumnDescriptionTagName = "[columnDescription]";  //列描述
            public const string PkColumnNameTagName = "[pkColumnName]";   //主键列名

      

    通过上面代码,我们可以将标签划分为三类

    1.占位符类,如[table],[pkColumnName],[columnDescription]

    2.语法类,如columnsEach,unlessLast

    3.筛选类 如dateColumn,pkColumn,textColumn,boolColumn……

           2.写模版文件 

    模版文件
    var [table]AddPostUrl = '/proc/[table]/?action=add';
    var [table]UpdatePostUrl = '/proc/[table]/?action=update';
    var [table]Form;
    var [table]IdStr;
    function initAdd[table]Form(containerid, idstr,rowObj) {
        if (!idstr)
            [table]IdStr = containerid;
        else
            [table]IdStr = idstr;
        [table]Form = new Ext.FormPanel({
            labelWidth: 100// label settings here cascade unless overridden
            url: [table]AddPostUrl,
            frame: true,
            bodyStyle: 'padding:5px 5px 0',
            buttonAlign: 'left',
            defaults: {  350 },
            defaultType: 'textfield',
            renderTo: containerid,
            items: [
    @columnsEach:        
    @pkColumn:
                {
                    xtype: 'hidden',
                    name: '[columnName]',
                    id: '[columnName]' + idstr,
                    value: null == rowObj ? null : rowObj.get('[columnName]'),
                    readOnly: true
                }@unlessLast:, @<unlessLast@<pkColumn
        @everyColumn:
                {
                    xtype: 'textfield',
                    fieldLabel: '[columnDescription]',
                    name: '[columnName]',
                    allowBlank: false,
                    value: null == rowObj ? null : rowObj.get('[columnName]'),
                    id: '[columnName]' + idstr
                }@unlessLast:, @<unlessLast@<everyColumn
        @dateColumn:
                {
                    xtype: 'datefield',
                    fieldLabel: '[columnDescription]',
                    name: '[columnName]',
                    allowBlank: false,
                    value: null == rowObj || !Ext.isDate(rowObj.get('[columnName]')) ? null : rowObj.get('[columnName]').dateFormat('Y-m-d H:i:s'),format:'Y-m-d H:i:s',
                    id: '[columnName]' + idstr
                }@unlessLast:, @<unlessLast@<dateColumn  
        @<columnsEach
                ],
                buttons: getAddOrEditButton('[table]')
        });

    @columnsEach:
        @fkColumn:
        $('#[columnName]' + idstr).focus(function() {
            loadGrid('[fkTable]'this, $('#hidden[columnName]' + idstr)[0],'[columnName]');
        });@<fkColumn

    @<columnsEach
                
                
    if (Ext.getCmp(containerid + '_tab'))
        Ext.getCmp(containerid + '_tab').add([table]Form);

    }

    看到这个模块,也就清楚如果写了,一个标记,我们得让程序知道从哪里开始,又从哪里结束,所以我这里以@tagName:开始,@<tagName结束,所以我的TagIndex类里也依赖这一点

    3.进行语法分析和替换动作

        这是很重要的一步了,要操作标签,首先我们得找到标签。很简单,一个IndexOf就足够了,但是我要稍微封装一下,如下: 

    找到标签的类
    using System;

    namespace TemplateEngine
    {
        public class TagIndex
        {
            public bool FindThisTag { get { return Start >= 0; } }
            public int Start
            {
                get
                {
                    return Source.IndexOf("@" + TagName, System.StringComparison.Ordinal);
                }
            }
            public int End
            {
                get
                {
                    return Source.IndexOf("@<" + TagName, System.StringComparison.Ordinal); ;
                }
            }
            public string Source { getset; }
            public string TagName { getset; }
            public string Content
            {
                get
                {
                    return Source.Substring(Start + string.Format("@{0}:", TagName).Length, End - Start - string.Format("@<{0}", TagName).Length);
                }
            }

            public string RealContent
            {
                get
                {
                    return Source.Substring(Start, End - Start + string.Format("@<{0}", TagName).Length);
                }
            }


            public static TagIndex GetTag(string str, string tagName)
            {
                var startOfAt = str.IndexOf("@" + tagName, System.StringComparison.Ordinal);
                if (startOfAt < 0)
                    return new TagIndex { TagName = tagName, Source = str };
                var endOfAt = str.IndexOf("@<" + tagName, System.StringComparison.Ordinal);
                if (endOfAt < 0throw new FormatException(string.Format("模版缺少@<{0}这一结束标记", tagName));
                return new TagIndex { TagName = tagName, Source = str };
            }
        }
    }

     标签找到之后,便是分析替换,像上面的[table](占位符类)标签,我们当然直接替换就可以了,但是语法类的我们还得稍加分析才行,拿columnsEach举例吧,上伪代码

      var forTag = TagIndex.GetTag(str, "columnEach");
                while (forTag.FindThisTag)
                {
                    var forStr = forTag.RealContent;
                    string fields = string.Empty;

                    MathTag mathTag = new MathTag(forStr);

                    columns.ForEach(m =>
                    {
                         //去替换里面的占位符类标签
                    }

                 }      

     再来看看unlessLast标签,定义这个标签的用意在于,最后一行就不输出标签里面的内容,使用场景如:给表格加入列时,最后一列我们不需要加逗号,见下图(当然不是只针对这种场景)

     

    如上图,第一处有“,” 第二处则没有,要实现这一点,我们在模版文件里这样写

    @everyColumn:
                {
                    xtype: 'textfield',
                    fieldLabel: '[columnDescription]',
                    name: '[columnName]',
                    allowBlank: false,
                    value: null == rowObj ? null : rowObj.get('[columnName]'),
                    id: '[columnName]' + idstr

                        }@unlessLast:, @<unlessLast@<everyColumn  

                

    可能有人会说,这也太大材小用了吧,呵呵,希望你能提出意见,我现在只想到这种方式。下面把所有代码粘过来,有需要的可以改改.

    Generator
    using System.IO;
    using System.Text;
    using XDbFramework;

    namespace TemplateEngine
    {
        public class Generator
        {
            public const string ColumnEachTagName = "columnsEach"//循环列
            public const string DateColumnTagName = "dateColumn";  //日期列
            public const string PkColumnTagName = "pkColumn";      //主键列
            public const string TextColumnTagName = "textColumn";  //文本列
            public const string BoolColumnTagName = "boolColumn";  //bool列
            public const string EveryColumnTagName = "everyColumn";//所有列
            public const string NumberColumnTagName = "numberColumn";//数字列
            public const string MoneyColumnTagName = "moneyColumn";  //money型列
            public const string ColumnNameTagName = "[columnName]";  //列名
            public const string UnlessLastTagName = "unlessLast";    //除非最后一个
            public const string TableTagName = "[table]";            //表名
            public const string FkColumnTagName = "fkColumn";        //外键列名
            public const string FkTableTagName = "[fkTable]";        //外键表名
            public const string ColumnDescriptionTagName = "[columnDescription]";  //列描述
            public const string PkColumnNameTagName = "[pkColumnName]";   //主键列名

            private readonly string _template;

            public Generator(string templatePath)
            {
                using (var reader = new StreamReader(templatePath, encoding: Encoding.UTF8))
                {
                    _template = reader.ReadToEnd();
                }
            }

            public string Generate<T>() where T : class
            {
                var table = DalHelper.GetTableInfo(typeof(T));
                var columns = DalHelper.GetTypeColumns<T>();
                var str = _template.Replace(TableTagName, table.TableName);
                var pkColumn = columns.Find(m => m.KeyType == KeyTypeEnum.PrimaryKey);
                if (pkColumn != null)
                    str = str.Replace(PkColumnNameTagName, pkColumn.ColumnName);

                var forTag = TagIndex.GetTag(str, ColumnEachTagName);
                while (forTag.FindThisTag)
                {
                    var forStr = forTag.RealContent;
                    string fields = string.Empty;

                    MathTag mathTag = new MathTag(forStr);

                    columns.ForEach(m =>
                    {
                        string tmpStr = string.Empty;
                        TagIndex tag = mathTag.GetTagIndexByColumn(m);
                        if (tag != null)
                        {
                            tmpStr = tag.Content
                                .Replace(ColumnNameTagName, m.ColumnName)
                                .Replace(ColumnDescriptionTagName, m.Description)
                                .Replace(FkTableTagName, m.ForeignKeyTableName);
                        }
                        var unlessLastTag = TagIndex.GetTag(tmpStr, UnlessLastTagName);
                        if (unlessLastTag.FindThisTag)
                        {
                            if (m == columns[columns.Count - 1])
                            {
                                tmpStr = tmpStr.Replace(unlessLastTag.RealContent, string.Empty);
                            }
                            else
                            {
                                tmpStr = tmpStr.Replace(unlessLastTag.RealContent, unlessLastTag.Content);
                            }
                        }
                        fields += tmpStr;
                    });

                    str = str.Replace(forStr, fields);
                    forTag = TagIndex.GetTag(str, ColumnEachTagName);
                }
                return str;
            }
        }
    }
    MathTag
    using System;
    using XDbFramework;

    namespace TemplateEngine
    {
        public class MathTag
        {
            private string _sourceString;
            private TagIndex pkTag;
            private TagIndex fkTag;
            private TagIndex textTag;
            private TagIndex dateTag;
            private TagIndex numberTag;
            private TagIndex moneyTag;
            private TagIndex boolTag;
            private TagIndex everyTag;


            public MathTag(string sourceString)
            {
                _sourceString = sourceString;
                pkTag = TagIndex.GetTag(sourceString, Generator.PkColumnTagName);
                fkTag = TagIndex.GetTag(sourceString, Generator.FkColumnTagName);
                textTag = TagIndex.GetTag(sourceString, Generator.TextColumnTagName);
                dateTag = TagIndex.GetTag(sourceString, Generator.DateColumnTagName);
                numberTag = TagIndex.GetTag(sourceString, Generator.NumberColumnTagName);
                moneyTag = TagIndex.GetTag(sourceString, Generator.MoneyColumnTagName);
                boolTag = TagIndex.GetTag(sourceString, Generator.BoolColumnTagName);
                everyTag = TagIndex.GetTag(sourceString, Generator.EveryColumnTagName);
            }

            public TagIndex GetTagIndexByColumn(ColumnAttribute m)
            {
                if (m.KeyType == KeyTypeEnum.PrimaryKey && pkTag.FindThisTag)
                {
                    return pkTag;
                }
                if ((m.CType == typeof(DateTime) || m.CType == typeof(DateTime?)) && dateTag.FindThisTag)
                {
                    return dateTag;
                }
                if ((m.CType == typeof(bool) || m.CType == typeof(bool?)) && boolTag.FindThisTag)
                {
                    return boolTag;
                }
                if ((m.CType == typeof(float) || m.CType == typeof(decimal) || m.CType == typeof(double) || m.CType == typeof(float?) || m.CType == typeof(decimal?) || m.CType == typeof(double?)) && moneyTag.FindThisTag)
                {
                    return moneyTag;
                }
                if ((m.CType == typeof(int) || m.CType == typeof(int?) || m.CType == typeof(long) || m.CType == typeof(long?)) && numberTag.FindThisTag)
                {
                    return numberTag;
                }
                if (m.CType == typeof(string) && textTag.FindThisTag)
                {
                    return textTag;
                }
                if (m.KeyType == KeyTypeEnum.ForeignKey && fkTag.FindThisTag)
                {
                    return fkTag;
                } if (everyTag.FindThisTag)
                {
                    return everyTag;
                }
                return null;
            }
        }
    }

     好,这里整个过程也就差不多结束了(不知道怎么替换?当然是Replace)

  • 相关阅读:
    纯css3实现旋转的太极图
    webstorm9.3 安装less 编译css教程
    javascript之查找数组中最小/最大的数
    javascript基础之打印乘法表
    javascript之查找数组元素
    jvascript 顺序查找和二分查找法
    Vue基础知识之常用属性和事件修饰符(二)
    Vue源码(一)
    BFC以及margin的深入探究
    jQuery中Ajax参数详细介绍
  • 原文地址:https://www.cnblogs.com/xianhong/p/2597121.html
Copyright © 2011-2022 走看看