模版,大家肯定都比较熟悉的一个概念,刚学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: { width:
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 {
get;
set; }
public
string TagName {
get;
set; }
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 <
0)
throw
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)