[转载]模版引擎AderTemplate源代码分析笔记

mikel阅读(1121)

[转载]模版引擎AderTemplate源代码分析笔记 – kwklover – 博客园.

概述

AderTemplate是一个小型的模板引擎。无论是拿来直接使用还是用来研究模板引擎实现方式,都是一个不错的选择。本文尝试对其源代码做一些分析。

数据流程

AderTemplate的数据处理流程可以总结为:
模版文件 -> 模版分析 -> Template对象 -> 分析处理TemplateElement集合 -> 输出目标文本

模版语法

简化描述如下

1,变量替换 :如#variable#

2,循环语句 <ad:foreach var=”x” collection=”#values#” index=”i”></ad:foreach>

3,判断语句 <ad:if test=”#value#”> </ad:if>

详细请参看AderTemplate的相关说明.

模版分析

模版分析的过程可以分成两步:
1
,把模版文件分析成Token

2,对Token流进行分析,形成Element集合

所以,首先要了解TokenElement的结构与异同。

Token的类结构:

AderTemplateToken.JPG

TokenKind(Token的类型)的详细说明:

TokenKind

说明

EOF

结束符

Comment

注释,但在AderTemplate没看到具体实现

ID

这个比较难描述,下面会通过输出Token流来直官了解下ID

TextData

文本数据,不包含模版语法的独立文本区

TagStart

Tag的开始标记,<ad: 注:Tag是一个Element,后面详述

TagEnd

Tag的结束符, > ,注意与TagClose的区别

TagEndClose

Tag的自闭结束符 ,即 />

TagClose

Tag的结束标记 </ad: <ad:对应,所以与TagEnd很容易区别

TagEquals

Tag=符号。与Tag无关的=属于TextData,TagEquals

ExpStart

AderTemplate中,语法#v#被定义为Expression.#ExpStart

ExpEnd

AderTemplate中,语法#v#被定义为Expression.#ExpEnd

LParent

左括号 (

RParent

右括号 )

Dot

即一个点 .

Comma

逗号 ,

Integer

数字

StringStart

文本的开始符,即

StringEnd

文本的结束符,即

StringText

How to description ?

为了获得更直观的认识。下面尝试把一个简单的模版文件分析的Token流输出。
模版文件,我们暂且命名为templateFile

<ad:foreach collection=”#GetBookList(3)#” var=”book” index=”i”>

<tr>

<td>#i#</td>

<td>#book.BookName#</td>

<td>#book.BookCount#</td>

</tr>

</ad:foreach>

分析的代码:

static void Main(string[] args)

{

string data = string.Empty;

using (System.IO.StreamReader reader = new System.IO.StreamReader(templateFile))

{

data = reader.ReadToEnd();

}

//模版分析,形成Token

TemplateLexer lexer = new TemplateLexer(data);

Console.WriteLine(“=======开始输出TemplateLexer分析出Token=======”);

do

{

Token token = lexer.Next();

Console.WriteLine(“\tToken类型:{0}\t行列:({1}, {2})\t数据:{3}”, token.TokenKind.ToString(), token.Line, token.Col

, token.Data.Replace(“\r\n”, “”));

if (token.TokenKind == TokenKind.EOF)

break;

} while (true);

Console.WriteLine(“=======结束输出TemplateLexer分析出Token=======\r\n”);

}

输出结果:

输出结果

对照表格的说明和输出结果。可以把Token理解为特定类型的文本区,即把模版文件按照TokenKind定义的类型分解成多个文本区(Token),所以分析模版文件形成的Token(集合)是一种相对线性的结构。是对模版文件的初步分析与处理。

下面继续说说Element的类体系:

AderTemplateElement.JPG

上述类关系图中可以看到Element主要有四个继承体系。

但每个类表达的意义是什么呢?

下面用一个表格做一个简单的说明。

类名

指代及其含义

Element

各种Element的基类

Text

文本类的Element , TokenKindTextDataToken相似的概念

TagClose

Tag结束符号

Tag

Tag是一个比较广的概念。上述的模版可以表示一个foreach tag

Expression

AderTemplate把语法类似#values#定义为一个Expression

FCall

Expression子类之一,表示该Expression是一个外部函数(Function)

FieldAccess

Expression子类之一,可以近似的理解成字段访问”,#book.BookName#

Name

Expression子类之一,可以近似的理解成外部变量”,#i#

StringLiteral

Expression子类之一,可以近似的理解成非数字型变量

IntLiteral

Expression子类之一,可以近似的理解成数字型变量

StringExpression

Expression子类之一,但其表示的是Expression的集合封装类

为了获得更直观的了解。我们可以通过分析上述的模版文件输出的Element来理解。因为此部分代码比较多。所以不列出了。后面会提供包含本文提到的所有代码的example.

这里需要说明的是Tag ElementTag是一个复合结构.Tag在不同的分析阶段,会些许不同。

如下面代码的(1)(2)分析出的Element是不同的。可通过输出结果来跟踪异同。

TemplateLexer lexer = new TemplateLexer(templateData);

TemplateParser parser = new TemplateParser(lexer);

List<Element> elems = parser.Parse(); –(1)

TagParser tagParser = new TagParser(elems);

elems = tagParser.CreateHierarchy(); –(2)

TokenElement异同分析:

在上面已经提到,Token是对模版文件的一种初步分析和分解。AderTemplate对模版文件的分析处理目标是把模版文件表达为一个Template对象。而Template对象的本质其实就是Element集合的封装类,也就是AderTemplate的对模版文件的分析其实是希望把模版文件在程序结构层面表达为一个Element的集合,从上面的ElementToken类关系图可以看出。Element远比Token复杂的多。如果直接从模版文件直接分析成Element集合结构的话,可以预见分析过程会变的相当复杂。所以AderTemplate引入了Token数据结构作为一种辅助分析手段。使得AderTemplate的对模版文件的分析过程变得更流畅和清晰了。程序的架构也变得相对优雅。所以我认为阅读AderTemplate的源代码,关键是理解TokenElement的结构和作用。特别是Element结构。因为它是AderTemplate的基础结构。即解决了在程序层面用什么结构来表达模版文件的问题。

模版分析涉及到的处理类:

TemplateLexer : 从模版文件中分析成Token流。

TemplateParser: 基于Token流分析成初步的Element集合结构。

TagParser : 把TemplateParser分析出的Element集合做最后的优化处理。

Template(模版)对象:

AderTemplate中。我们可以简单地认为一个Template就是经过模版分析后形成的最终的Element集合的封装体。

Template(模版)处理:

TemplateElement集合进行分析。生成目标文本。

此过程涉及到的技术其实不复杂,就是反射和委托,所以本文不打算详细分析此过程。否则篇幅过大。而且没什么意义。在后面我会提供一个模拟AderTemplateElement分析处理到生成目标文本过程的例字。

相关例子

为了辅助理解AderTemplate的整个处理过程。我写了几个例子。
1
,输出Token的结构信息。对比模版文件辅助理解Token的结构

2,输出从Token流中分析出的Element集合的结构信息。以辅助理解Element的结构。

3,模拟从Element集合中生成目标文本的过程。辅助理解从Element集合到生成目标文本的整个处理流程和过程。这个过程是相对简单的。从这个角度来看,AderTemplateElement集合来表达模版文件是一个比较优秀的设计。

至于如何从模版文件分析成Element集合(Template对象)则是一个相对复杂的过程了。最好的解释还是源代码本身了。所以就不在此不做更多的分析了。

总结

AderTemplate是一个小型的模版引擎。功能并不算强大。但基本功能已经具备。从应用的角度来说,可能未必是一个最好的选择。但想学习模版引擎的开发。确是一个很不错的选择。我个人认为其架构还是比较优秀的。而且数据处理流程也很清晰。

当写作本文的时候,我阅读AderTemplate的源代码已有好些天了。依然感觉似懂非懂。到是写本文让我把思路给理清了。所以有时候。我觉得。写笔记也是一种学习。:)


AderTemplate与相关例子代码下载

[转载]推荐一个模板引擎templateengine

mikel阅读(1063)

[转载]推荐一个模板引擎 – templateengine – 流浪的风(专注于.NET2.0技术) – 博客园.

一直都在使用StringTemplate模板引擎,虽然使用方便,但是功能实在太弱太弱,准备放弃使用StringTemplate。刚好碰巧发现了另外一个开源的模板引擎,就是今天要介绍的TemplateEngine 2,功能非常强大。

先看看他的语法吧

Thank You for your order #order.billFirstName# #order.billLastName#.
<br>
Your Order Total is: #format(order.total, “C”)#
<br>
<ad:if test=”#order.shipcountry isnot “US”#”>
Your order will arrive in 2-3 weeks
<ad:else>
Your order will arrive in 5-7 days
</ad:if>

TemplateEngine2.0 主要功能

Template Engine主要是两个类在使用:Template 和TemplateManager,Template 是一个基本模板, TemplateManager是扩展的模板

用Template或TemplateManager 是非常容易创建模板的

Template template = Template.FromString(string name, string data)
Template template
= Template.FromFile(string name, string filename)

用TemplateManager

TemplateManager mngr = new TemplateManager(Template template);

或者

TemplateManager mngr = TemplateManager.FromFile(string filename);
TemplateManager mngr
= TemplateManager.FromString(string template);

当调用FromString 方法时,string是模板的代码。这个方法能动态的加载文本到template中。FormFile是指调用的模板文件。

TemplateManager有个属性SetValue可以用来设置模板的变量和变量的内容

mngr.SetValue(xx, xxxxxxxxxxxxxx);

输出模板

Response.Write(mngr.Process());

这一切都非常的简单。

函数列表

equals(obj1.obj2) 比较两个变量是否一样,返回bool型
notequals(obj1,obj2)比较两个变量是否不一样,返回 bool型 也可以 not(equals(obj1,obj2))
iseven(num) 测试是否是偶数
isodd(num)测试是否奇数
isempty(string) 测试字符串是否为0
isnotempty(string) 测试字符串是否大于0
isnumber(num) 测试是否为整型
toupper(string) 转化成大写
tolower(string) 转化成小写
isdefined(varname) 测试变量是否有定义
ifdefined(varname, value) 返回 value 如果varname 是有定义的,
特别有用:#ifdefined(“name”, name)# 将输出value 如果varname没有定义,其它情况不输出
len(string)返回字符串长度
tolist(collection, property, delim) 简单的输出列表

例1:

ArrayList list = new ArrayList();
list.Add(
one);
list.Add(
two);
list.Add(
three);
template.SetValue(
mylist, list);

你的模板内容:
#toList(mylist, ” & “)#
输出:one & two & three

例2:

list.Add(new Customer(Tom, Whatever));
list.Add(
new Customer(Henry, III));
list.Add(
new Customer(Tom, Jackson));
template.SetValue(
mylist, list);

模板内容:
#toList(mylist, “firstName”, “,”)#
输出:Tom,Henry,Tom

函数列表

snull(obj)
测试是否为空
not(boolvalue) 返回not(!)的bool型
iif(booleanExpression, iftruevalue, iffalsevalue) 如果booleanExpression为真,输出iftruevalue,否则输出iffalsevalue
format(object, formatstring)格式化字符串,相当于ToString(formatstring)

例子:

#format(total, “C”)#
will output: $1,208.45

函数列表

trim(string)
去除前后空格
filter(collection, booleanproperty) 返回一个新的组所有符合booleanproperty要求的
gt(obj1,obj2) 如果 obj1 > obj2 返回为真,否则为false
lt(obj1,obj2) 如果 obj1 < obj2 返回为真,否则为false
compare(obj1, obj2) 如果 obj1<obj2 返回-1 obj1=obj2 返回0 如果obj1>obj2 返回1
or(bool1,bool2) 有一个为真返回就是true
and(bool1, bool2) 两个都为真时返回true
comparenocase(string1, string2) 相等时返回为true 否则为false
stripnewlines(string) 返回 所有\r\n 用space 代替的字符串
typeof(object) 返回object 类型,如typeof(“abcd234”) 返回 “string” typeof(3) 返回 int
cint(value) 返回整数 等同于Convert.toInt32()
cdouble(value) 返回双精度
cdate(value) 返回一个datetime 型
createtypereference(type) 引用一个静态的类型,但格式必须是<ad:set tag

例:
#createtypereference(“System.Math”).Round(3.39789)#
#createtypereference(“System.Math”).PI#
or
<ad:set name=”MyMath” value=”#createtypereference(“System.Math”)#” />
#MyMath.Round(3.3)#
#MyMath.PI#

还支持C#内置的一些方法,如还可以这样使用
#xx.Length#

还有很多你意想不到的效果,赶紧来试用吧。

官方地址:http://www.adersoftware.com/index.cfm?page=templateEngine2

[转载]使用vbs借助mspaint.exe实现图片无损压缩

mikel阅读(1039)

[转载]使用vbs借助mspaint.exe实现图片无损压缩_勇敢的心_百度空间.

有的时候四处寻觅的好东西可能就在眼前!由于想要换一个网站的Banner,为了能把图片压缩到极致,于是四处寻找好用的图片压缩工具,试用了一些软件后 才发现,原来最好的图片压缩工具其实就是微软winXP系统下自带的MSpaint.exe(画图程序)。

美中不足的是Mspaint无批量处理功能,只能逐个压缩,这给我们需要处理大量图片的朋友带来了不小困难。笔者就此问题,在网上也找了若干的解决方案,发现借助vbs可以很好地解决此问题;参照其他网友的实现,笔者又做了少许加工,完成了如下vbs脚本:

‘**********************************************
‘*使用说明,选择源文件夹和目标文件夹
‘*不支持中文路径和文件名
‘**********************************************

Dim FileName,fs,srcFolder,disFolder
Const PICTURE_TYPE = “.jpg.gif.jpeg”
Const MY_COMPUTER = &H11&
Const WINDOW_HANDLE = 0
Const OPTIONS = 0
Set objShell = CreateObject(“Shell.Application”)
Set objFolder = objShell.Namespace(My_Computer)
Set objFolderItem = objFolder.Self
srcFolder = objFolderItem.Path

‘**********************************************
‘*选择源文件夹
‘**********************************************
Set objFolder = objShell.BrowseForFolder(WINDOW_HANDLE, “选择源文件夹:”, OPTIONS, “”)
If objFolder Is Nothing Then
msgbox “您没有选择任何有效目录!”
wscript.quit
End If

Set objFolderItem = objFolder.Self
srcFolder = objFolderItem.Path

If HasChinese(srcFolder) Then
msgbox “不支持中文路径,请重新选择!”
wscript.quit
End If

‘**********************************************
‘*选择输出文件夹
‘**********************************************
Set objFolder = objShell.BrowseForFolder(WINDOW_HANDLE, “选择输出文件夹:”, OPTIONS, “”)
If objFolder Is Nothing Then
msgbox “您没有选择任何有效目录!”
wscript.quit
End If

Set objFolderItem = objFolder.Self
disFolder = objFolderItem.Path

If HasChinese(disFolder) Then
msgbox “不支持中文路径,请重新选择!”
wscript.quit
End If
Call main

Sub main
On Error Resume Next
set WshShell = WScript.CreateObject(“WScript.Shell”)
WshShell.Run “C:\WINDOWS\system32\mspaint.exe”
WScript.Sleep 1000
WshShell.AppActivate “paint”
WScript.Sleep 1000

Dim objfso,objfolder1
Set objfso = CreateObject(“scripting.filesystemobject”)
Set objfolder1 = objfso.getfolder(srcFolder)

For Each objfile In objfolder1.files
If AllowExtension(LCase(objfso.GetExtensionName(objfile))) =true Then

WshShell.Sendkeys “^o”
WScript.Sleep 100
path=TrimLast(srcFolder,”\”) +”\”+ objfile.name
WshShell.SendKeys path
WScript.Sleep 100
WshShell.SendKeys “~”
WScript.Sleep 200
‘另存为
WshShell.Sendkeys “%f”
WshShell.Sendkeys “a”
WScript.Sleep 100
WshShell.Sendkeys “{BS}”
path=TrimLast(disFolder,”\”) +”\”+ objfile.name
WshShell.SendKeys path
WScript.Sleep 100
WshShell.Sendkeys “~”
WScript.Sleep 200

End If
Next

End Sub
‘**********************************************
‘*检查是否含有中文
‘**********************************************
Function HasChinese(sFileName)
Set regEx = New RegExp
regEx.Pattern = “^[\x00-\xff]*$”
regEx.IgnoreCase = True
HasChinese = Not regEx.test(sFileName)
End Function

‘**********************************************
‘*检查是否允许的扩展名
‘**********************************************
Function AllowExtension(sFileName)
If IsNull(sFileName) Or sFileName = “” Then
AllowExtension = False
else
AllowExtension = InStr(PICTURE_TYPE,sFileName)>0
End If
End Function

‘****
‘* Remove “chr” (if it exists) from end of “str”.
‘****
Function TrimLast(str,chr)
TrimLast = str
If Right(str,1) = chr Then
TrimLast = Left(str,Len(str)-1)
End If
End Function

直接下载:批量压缩图片.vbs

[转载].net c# 一个简单但是功能强大动态模板引擎

mikel阅读(988)

[转载].net c# 一个简单但是功能强大动态模板引擎 – DotNet Software Project – 博客园.

net C# 一个简单但是功能强大动态模板引擎(一) 收藏
注意:欢迎转载,但是请注明出处.

流行的模板引擎有很多,譬如Velocity.但是他的模板语言比较简,复杂的功能比较难实现,而且编辑模板比较麻烦容易出错.

但是利用UserControl就可以实现功能强大的一个动态模板引擎,编辑的模板的时候跟编辑一个用户控件没有区别,并且支持任何.net语言譬如C#.

下面就是代码:

view plaincopy to clipboardprint?
using System;
using System.Collections.Generic;
using System.Text;

namespace Template
{
public class TemplateBody : System.Web.UI.UserControl
{
private IDictionary<string, object> _context = new Dictionary<string, object>();
protected void Page_Load(object sender, EventArgs e)
{

}

public IDictionary<string, object> ViewData
{
get { return _context; }
}
}
}
using System;
using System.Collections.Generic;
using System.Text;

namespace Template
{
public class TemplateBody : System.Web.UI.UserControl
{
private IDictionary<string, object> _context = new Dictionary<string, object>();
protected void Page_Load(object sender, EventArgs e)
{

}

public IDictionary<string, object> ViewData
{
get { return _context; }
}
}
}

TemplateBody类基本没什么代码只是声明了一个ViewData属性,该属性用于向模板添加数据由模板来获取并展示.

view plaincopy to clipboardprint?
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Web.UI;

namespace Template
{
public class TemplateEngine:IDisposable
{
private UserControl _uc;
private TemplateBody _tpl;

public TemplateEngine()
{
_uc = new UserControl();
}
/// <summary>
/// 加载一个模板
/// </summary>
/// <param name=”path”>这个路径为相对路径</param>
public void Load(string path)
{
_tpl = _uc.LoadControl(path) as TemplateBody;
if (_tpl == null)
{
throw (new ArgumentException(path));
}
}
/// <summary>
/// 控制展示
/// </summary>
/// <returns>返回生成的字符串</returns>
public string Render()
{
TextWriter tw = new StringWriter();
Render(tw);
return tw.ToString();
}
/// <summary>
/// 展示模板
/// </summary>
/// <param name=”writer”>TextWriter对象,可以传Response.Output</param>
public void Render(TextWriter writer)
{
HtmlTextWriter htw = new HtmlTextWriter(writer);
_tpl.RenderControl(htw);
}
/// <summary>
/// 增加一个显示数据的上下文
/// </summary>
/// <param name=”key”></param>
/// <param name=”obj”></param>
public void AddContext(string key, object obj)
{
_tpl.ViewData.Add(key, obj);
}

public object this[string key]
{
get{
object ret;
_tpl.ViewData.TryGetValue(key, out ret);
return ret;
}
set { AddContext(key, value); }
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (disposing)
{
_uc.Dispose();
}

}
~TemplateEngine()
{
Dispose(false);
}
}
}
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Web.UI;

namespace Template
{
public class TemplateEngine:IDisposable
{
private UserControl _uc;
private TemplateBody _tpl;

public TemplateEngine()
{
_uc = new UserControl();
}
/// <summary>
/// 加载一个模板
/// </summary>
/// <param name=”path”>这个路径为相对路径</param>
public void Load(string path)
{
_tpl = _uc.LoadControl(path) as TemplateBody;
if (_tpl == null)
{
throw (new ArgumentException(path));
}
}
/// <summary>
/// 控制展示
/// </summary>
/// <returns>返回生成的字符串</returns>
public string Render()
{
TextWriter tw = new StringWriter();
Render(tw);
return tw.ToString();
}
/// <summary>
/// 展示模板
/// </summary>
/// <param name=”writer”>TextWriter对象,可以传Response.Output</param>
public void Render(TextWriter writer)
{
HtmlTextWriter htw = new HtmlTextWriter(writer);
_tpl.RenderControl(htw);
}
/// <summary>
/// 增加一个显示数据的上下文
/// </summary>
/// <param name=”key”></param>
/// <param name=”obj”></param>
public void AddContext(string key, object obj)
{
_tpl.ViewData.Add(key, obj);
}

public object this[string key]
{
get{
object ret;
_tpl.ViewData.TryGetValue(key, out ret);
return ret;
}
set { AddContext(key, value); }
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected void Dispose(bool disposing)
{
if (disposing)
{
_uc.Dispose();
}

}
~TemplateEngine()
{
Dispose(false);
}
}
}

TemplateEngine 是负责显示的类,核心代码也就是调用了RenderControl函数.

下面是具体使用:

1.创建一个web工程,注意其他工程可能不支持.

2.添加默认页面Default.aspx

3.根目录添加一个TemplateTest.ascx的模板文件 扩展名默认是ascx,如果需要更改别的扩展名的话需要在web.config里在compilation节点增加下列代码:

view plaincopy to clipboardprint?
<buildProviders>
<add extension=”.view” type=”System.Web.Compilation.UserControlBuildProvider”/>
</buildProviders>
<buildProviders>
<add extension=”.view” type=”System.Web.Compilation.UserControlBuildProvider”/>
</buildProviders>

4.直接运行就可以.

Default.aspx代码:

view plaincopy to clipboardprint?
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;

using Template;

namespace TemplateDemo
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
TemplateEngine te = new TemplateEngine();
te.Load(“TemplateTest.ascx”);
te.AddContext(“Text”, “Super Man”);
te.Render(Response.Output);
}
}
}
using System;
using System.Collections;
using System.Configuration;
using System.Data;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;

using Template;

namespace TemplateDemo
{
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
TemplateEngine te = new TemplateEngine();
te.Load(“TemplateTest.ascx”);
te.AddContext(“Text”, “Super Man”);
te.Render(Response.Output);
}
}
}
TemplateTest.ascx代码:

view plaincopy to clipboardprint?
<%@ Control Language=”C#” AutoEventWireup=”true” CodeBehind=”TemplateBody.cs” Inherits=”Template.TemplateBody” %>

<% for (int i = 0; i < 10; i++){
%>
<%=ViewData[“Text”]%>
<% }%>
<%@ Control Language=”C#” AutoEventWireup=”true” CodeBehind=”TemplateBody.cs” Inherits=”Template.TemplateBody” %>
<% for (int i = 0; i < 10; i++){
%>
<%=ViewData[“Text”]%>
<% }%>

记住,模板必须要加这个头:

<%@ Control Language=”C#” AutoEventWireup=”true” CodeBehind=”TemplateBody.cs” Inherits=”Template.TemplateBody” %>

下面是显示结果:

Super Man Super Man Super Man Super Man Super Man Super Man Super Man Super Man Super Man Super Man

需要改进的地方:

目前模板只支持相对路径,因为.net他对文件进行缓存处理,这样运行一次模板后即编译模板并进行缓存,如果文件被更改将重新编译,提高效率.

如果您需要从数据库或者从一个Stream里加载模板的话,需要重写VirtualPathProvide,并且重写判断模板被更改的函数 CacheDependency,还有获取虚拟文件的函数GetFile, 这样很容易实现从任何地方获取模板.如果您有兴趣可以进行改进,完善.

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/baoaya/archive/2009/07/27/4384178.aspx

[转载]jtemplates jquery模板引擎使用

mikel阅读(1029)

[转载]jtemplates jquery模板引擎使用 – 蛰穴灵异 技术博客 – 博客园.

工作中需要在前端将json数据源绑定到页面html代码中,之前一直用组合html代码的形式编写,不太容易维护。Ms ajax4.0据说有一套模板引擎,只有预览版,而且看来比较复杂,不太能马上用。在网上找到一个jtemplates的js模板引擎,感觉还可以,适合 用来在页面上动态绑定数据。

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Transitional//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head runat=”server”>
<title>Index</title>
<script src=”/Scripts/JQuery-1.3.2.js” type=”text/JavaScript></script>
<script src=”/Scripts/plugins/JQuery-jtemplates.js” type=”text/JavaScript></script>

<script type=”text/javascript”>
$(document).ready(
function() {
var data = [{ name1: 测试信息,id:12 },{ name1: 测试信息,id:12 }];
$(
#result).setTemplateElement(template1);
$(
#result).processTemplate(data);
});
</script>
</head>
<body>
<div>
<textarea id=”template1″ style=”display: none;”>
<table>
<thead>
<tr>
<td colspan=”2″>
fdaasfd
</td>
</tr>
</thead>
<tbody>
{#foreach $T as r}
<tr>
<td>
name:{$T.r.name1}
</td>
<td>
id:{$T.r.id}
</td>
</tr>
{#/for}
</tbody>
<tfoot>
</tfoot>
</table>
</textarea>
</div>

<div id=”result”></div>
</body>
</html>

[转载]从模板引擎到模板引擎-使用aspx页面作为模板引擎的一种实现

mikel阅读(939)

[转载]从模板引擎到模板引擎-使用aspx页面作为模板引擎的一种实现 – 懒人居 – Coding for fun – 博客园.

此文完全有感而发,在网上看到很多介绍各类模板引擎的文章,但是我却越来越感觉到很多时候我们所做的事
情是在舍近求远。
什么是模板引擎?说白了就是能够自动替换占位符的替换引擎。原理上也就是两个步骤,找到-》替换。但是替换不是简单的替换,包括:

  • 简单变量替换();
  • 复杂变量替换();
  • 对象变量替换();
  • 键值类型替换();
  • 自定义集合替换();
  • 同时显示多个变量();
  • 模板调用();
  • 给调用模板传参数();
  • 值模板();
  • 简单循环();
  • 交差循环显示();
  • 通过模板交差循环();
  • 条件判断();
  • 从文件中创建模板();

常用的模板引擎,StringTemplate和Velocity差不多都是按照这种模式来设计的。但是这样子真的就是我们需要的吗?我们拿一段典型的StringTemplate的模板代码来看看:

首先是在模板文件中:

姓名:$User.Name$ 年龄:$User.Value$

然后是在代码里:

User us = new User();
us.Name
= xxsssx;
us.Value
=80;
StringTemplate st
= new StringTemplate($User.Name$,$User.Value$);
st.SetAttribute(
User, us);
Console.WriteLine(st.ToString());

那么我们来和ASP.NET本身的代码做一个比较
aspx页面
<asp:Label id=”Name” runat=”Server” /><asp:Label id=”Mail” runat=”Server” />
aspx.cs文件

User us = new User();
us.Name
= xxsssx;
us.Value
=80;
Name.Text
=us.Name;
Mail.Text
=us.Value

是不是有点感觉用了模板引擎跟脱了裤子放屁没有什么区别?
那么我们为什么要用模板引擎?是真的需要还是赶时髦?
就我个人来看,模板引擎是需要的。视图和控制的分离是必要的,但是我们要分清楚什么是视图,什么是控制。是不是视图就不能包含代码和逻辑?肯定答案是否定 的,因为就算用了StringTemplate,你还是需要给它填充数据,而给它填充数据的代码仍然是属于视图而不是控制或者有人干脆就当作了模型。那么 既然这样,为什么我们就不能用aspx页面本身来作为模板呢?它可以判断,可以循环,可以分支,可以使用现成的控件。比如

<%
if(Helper.IsLogin()){
User u=Helper.GetLoginedUser();
%>
你的姓名是:<%=u.Name%>
<%}%>

我们把这种方法和StringTemplate的方法来比较一下。

aspx模板    StringTemplate
ASP.NET控件    支持           不支持
复杂显示逻辑    支持          支持(未经测试所有可能性)
编辑器支持       支持          不支持
编译检测          支持           不支持

现在可能有个疑问了,aspx的页面怎么作为模板来被调用呢?以访问就直接看到了没办法生成静态页面啊?
其实解决的方法很简单,只需要Server.Execute()就可以将执行的aspx页面运行的结果以一个TextWriter返回。这样子要怎么处理还不是看我们怎么高兴啦。哈哈

记得之前有人提过类似方法,不过有人可能会质疑:那不是还有美工也会asp.net?其实回过头一想,如果你告诉美工<%=u.Name%> 就是代表用户的姓名,那么也不会需要美工学习全套的asp.net,而其实就美工来说,如果学习简单实用asp.net都有困难,那么学习 StringTemplate的全套模板语法和灵活掌握使用方法可能更加让人郁闷。如果这些工作都给程序员来完成的话,那么已经是程序员熟练掌握的C#语 法是不是更加的平易近人呢?况且还可以智能感知菜单和编译检测支持。所以我觉得在大多数情况下这种方法是比较爽的一种方式。

[转载]用Visual Studio实践敏捷测试(三)下

mikel阅读(1170)

[转载]用Visual Studio实践敏捷测试(三)下 – 服务世界 开发未来 – 博客园.

自动化测试的实现

编 写自动化测试也许对很多测试人员来说比较陌生。所幸的是Visual Studio中为实现自动化测试提供了一系列的工具,单元测试(Unit Test)、编码UI测试(Coded UI Test)、压力测试(Stress Test)、网页性能测试(Web Performance Test)、数据库单元测试(Database Unit Test)等等,让实现自动化测试变得轻松。这里我想着重介绍2种最基本的,也是在我们的产品开发中最常用的测试:单元测试和编码UI测试。

1. 单元测试

单元测试是Visual Studio中最基本、应用最广泛的一种测试。通常开发人员可以选择为一个方法或是一个部件创建单元测试,来保证其逻辑正确。

要 在Visual Studio中创建单元测试,可以在源代码的上下文菜单中选择“创建单元测试”,并在弹出的窗口中选择需要为其创建单元测试的方法(如图一、图二所示)。 这样Visual Studio就会自动创建出一系列单元测试的代码框架,以及针对private/internal等无法直接调用的方法的访问器(Accessor),用 户只需修改或添加具体测试逻辑即可。访问器会随着源代码的每一次编译自动更新,为用户节省了不少麻烦。当然,用户也可以使用单元测试向导创建,或是直接添 加一个单元测试(测试->新建测试)文件再自行添加逻辑代码。

clip_image002

图一 创建单元测试

clip_image004

图二 创建单元测试对话框

单 元测试通常以[TestClass]属性来表示一个测试类,在测试类中使用5种不同的属性标示方法: [ClassInitialize]、[TestInitialize]、[TestMethod]、[TestCleanup]、[ClassCleanup]。 一个测试类中可包含多个测试方法(Test Method),但是仅可以有一个类初始化方法(Class Initialize)、一个测试初始化方法(Test Initialize)、一个测试清理方法(Test Method)、一个类清理方法(Class Cleanup)。在测试运行时,类的初始化会被首先调用,然后在运行每一个测试方法之前运行测试初始化,之后运行测试清理,在测试方法运行结束后,类清 理方法将被运行。除测试方法外,其他的辅助方法都不是必须的。大家可以根据实际需要来安排代码逻辑。

成 功编译后,所有测试方法都会在测试视图(Test View)窗口中列出,在该窗口中还可以对测试方法进行过滤、查询和排序,选择一个或多个测试方法后,可以运行或调试测试用例。测试的结果(是否通过)会 显示在测试结果(Test Result)窗口中,双击任意一条测试结果都会打开具体的测试结果日志以获取更详细的信息,如图三所示。单元测试还可以通过直接在测试方法代码中右键选 择“运行测试”,或是在命令行中直接执行mstest命令来运行。

clip_image006

图三 测试视图和测试结果

此外,单元测试工具不仅可以用作单元测试的目的,也可以作为一种载体,来实现验收测试或是功能测试。我们在实践中大量利用了Visual Studio对单元测试的管理、运行、日志等功能,通过在测试代码中实现验收测试、功能测试的具体逻辑来完成各种不同类型的测试。

2. 编码UI测试

虽 然单元测试框架适用于各种不同的测试,不过其本身却没有提供太多对测试代码实现上的支持。对于自动化测试中常常令人无从下手的UI操作的自动 化,Visual Studio 2010中添加了一种新的测试类型——编码UI测试,以帮助用户克服这一难题。编码UI测试是一种能轻松上手,迅速创建出UI测试的框架。

一 种最简单的创建UI测试的方法是直接从手动测试入手。如果此前我们曾在Test Manager中创建了测试用例,并曾在手动执行时录制过其测试步骤,那么我们就可以直接将录制的步骤转化为编码UI测试的代码。在Visual Studio中选择创建一个编码UI测试后,会跳出一个对话框询问用户是使用已有的操作录制还是重新录制,选择第二项“Use an existing action recording(使用现有操作录制)”后即可通过查询测试用例工作项将相应的测试转化为自动化测试代码(见图四)。

clip_image008

图四 创建编码UI测试

如 果之前没有录制过测试步骤,或是想重新创建测试的话,可以在图四对话框中选择第一项“Record actions, edit UI map or add assertions(录制操作、编辑 UI 映射或添加断言)”,这样编码UI测试生成器(Coded UI Test Builder)就会出现。在编码UI测试生成器中,用户可以自由选择为测试录制操作步骤(图五)、手动添加某些UI控件或是断言(图六),然后就可以为 这些内容生成代码。这一过程可以通过在代码的上下文菜单中选择“Generate Code for Coded UI Test(为编码UI测试生成代码) ”反复执行,需要提醒用户的一点是每一次所有的代码都将被重新生成,所以手动修改生成的代码是没有意义的,除非此后不再借助编码UI测试生成器生成代码。

clip_image009

图五 编码UI测试生成器——录制

clip_image011

图六 编码UI测试生成器——添加UI控件和断言

此外,用户还可以不借助Visual Studio提供的这些工具,直接利用编码UI测试提供的API(Microsoft.VisualStudio.QualityTools.CodedUITestFramework等)编写代码,实现UI自动化测试。

编码UI测试的运行方法、运行结果等都与单元测试类似,此处不再赘述。

这 里要强调的是自动生成的自动化UI测试并不能解决UI测试固有的不稳定的问题。尤其是这种编码UI测试是通过UI控件之间的包含关系来寻找控件并对其执行 操作的,就导致了如果运行测试时UI排列与录制时不尽相同时,测试可能无法正确运行。确保运行时UI环境的一致、在各操作步骤之间添加对UI控件状态的判 断、在生成的代码的基础上编写自己的代码是能提高编码UI测试稳定性的一些方法。

3. 其他类型测试

除 了上述两种常用的测试类型之外,Visual Studio针对不同类型的测试以及测试对象,提供了各种其他的测试工具。例如,网页性能测试通过记录用户每一步操作选择的地址和发送的信息来实现网页测 试的自动化;负载测试帮助用户模拟多用户各种不同测试环境下的负载;数据库单元测试提供了直接针对数据库的测试支持。这里我就不再一一详细介绍了,有兴趣 的读者可以自己在MSDN上查询使用方法或者直接试用这些功能。

自动化测试的管理

对 于手动测试,测试用例工作项已经能很好的描述测试的内容以及记录测试的结果。而自动化测试的不同之处在于其需要代码的支持。我们通常将测试代码和产品代码 一起保存在Team Foundation Server的源代码控制中,这样一方面便于代码的统一管理,另一方面让测试用例也能利用到TFS提供的版本控制、搁置集等功能。另外,我们还可以通过设 置TFS的测试用例工作项中包含的“关联的自动化测试”域的值将测试计划中的测试用例和实际的代码联系起来。

小结

在 这一篇中,我们讨论了手动测试和自动化测试各自的优势和局限性,两者互补和平衡能帮助测试人员更好的在敏捷开发的环境中完成测试任务。此外,我们还了解了 如何借助Visual Studio中提供的一些工具来实现并管理自动化测试。在介绍了自动化测试的方法和工具后,我将在下一篇中进一步为大家介绍如何计划和执行自动化的测试用 例。

林俊彦

软件测试开发工程师

[转载]用Visual Studio实践敏捷测试(三)上

mikel阅读(1181)

[转载]用Visual Studio实践敏捷测试(三)上 – 服务世界 开发未来 – 博客园.

上一篇中, 我们讨论了敏捷开发流程中的一些由手动执行的测试任务。手动测试是需要人工完成的测试,被广泛应用于各类产品的各种测试任务中,而与之相对应还有自动化测 试,即通过程序自动运行完成测试任务。自动化测试能帮助开发团队节省测试运行的人工、提高开发效率。接下来在本篇中,我想和大家讨论一下敏捷开发中手动测 试和自动化测试之间的关系以及如何实现和利用自动化测试。

手动测试的特点

由于手动测试依赖于人工操作,很自然的存在着不确定性,每一次的操作都可能或多或少有一些不同。这种不确定性既是手动测试的优点,也为其带来了一定的局限 性。从优点的角度来说,手动测试更加灵活多变,可能在不经意间就采用了一种全新的操作序列,或者将产品带入一个意料之外的状态。这有助于深入测试产品功能 的细节,覆盖那些常规思路无法到达的用户场景。而从另一个角度来看,手动测试的不确定性也使得其测试覆盖的内容无法预料,在需要保障测试的覆盖面时,就显 得有些不足了。此外,手动测试也不适应于大规模、大数据量、长时间或者是多平台的测试,人工完成这样大的工作量是困难且没有必要的。

手动测试的这些特点就导致其适用于那些允许灵活甚至是要求灵活的小规模的测试任务,比如伙伴测试就是个很好的例子——测试内容不那么死板,期望测试执行人有一些创造性。

自动化测试

自动化测试充分利用了计算机“任劳任怨”执行人类给它的指示的特点,用程序实现需要的测试,此后该测试就可以被多次自动执行。在测试会被大量运行的前提 下,这不仅能节省重复人工劳动,更重要的是提供了一种迅速而准确的保证测试覆盖的方法。特别是在敏捷开发紧张的节奏下,自动化测试的这些特质能起到十分显 著的加快开发进程的作用。

那么什么样的测试需要自动化呢?

最显然的是诸如压力测试等需要大量操作的测试场景。比如我们常常要求产品能承受连续8个小时以上的各种操作,这样大强度的压力测试并不适合人由工完成,显然自动化测试能更好的完成该项任务。

接下来是那些核心的功能和主要的用户场景,这些是产品最需要保护的部分,所以我们希望能在代码改变的情况下及时地运行相关的测试,以保证它们不被破 坏。在敏捷开发高速运转、频繁签入的状态下,自动化这些测试以便能将其在封闭签入(Gated check-in)等测试运行中迅速自动执行、及时发现问题,能在很大程度上保证产品开发流程的顺利进行。同时,将这些重要的测试自动化也能避免手动测试 的“偷工减料”问题,计算机总是会忠实的执行每一个测试步骤的。

此外还有一些从其本身特性而言不适合手动执行的特殊类型的测试,比如模糊测试(Fuzzing Test)要求大量生成随机数据一一对产品试验,也是自动化测试能大展身手的地方。

自动化测试的度

在这里我想提醒大家的是测试的自动化的程度并不是越高越好。在我的开发团队中,我们曾经将测试计划中高达95%以上的测试都自动化。这样的带来的显著优势 是自动的测试运行提供了很好的覆盖率,从而保证了各项功能都能保持正确工作。但是,也带来了一些问题:首先,编写自动化测试程序需要大量的时间,很多时候 让程序自动操作执行产品功能特别是产品UI是件很困难的事情,需要大量的时间和精力来实现测试程序本身。其次,当产品功能变化时,修改测试还要花费额外的 成本,不像手动测试那样随时都可以更改。另外,自动化测试还有一个容易被忽视却十分重要的问题——远离了用户体验。当测试被“傻瓜式”自动执行时,我们根 本无法得知其相对应的用户场景的使用感。举个简单的例子,再不方便使用的功能如果只写一次程序我们多半是可以忍受的,但是如果要手动执行,恐怕三五次就会 让人受不了了。所以把握手动测试和自动化测试的比例是颇值得研究的一个话题。

在意识到过量的自动化测试会使其从辅助开发的工具变为团队的负担和障碍之后,我们调整了自动化测试的策略,采取了一种渐进式添加自动化测试的方案。在产品 生命周期的前期,功能快速的被添加且行为随时可能改变,此时对测试的需求偏向于尽可能迅速完成对新功能的测试,自动化测试所需的建立测试框架以及在功能改 变时修改代码的代价在此时显得过于昂贵了,所以我们更倾向于做较多手动测试,只实现少量简单而核心的自动化测试。而随着产品功能的不断添加和趋于稳定,不 再有大量的新功能需要测试,而更多的需要用于保护已有功能不被破坏的自动化测试,此时可以陆续添加测试计划中那些之前没有时间自动化的测试用例。在产品生 命周期的末期,特别是产品将有多个版本时,准备全面的自动化测试覆盖以保护产品功能不被破坏则成为主要的测试任务之一。

林俊彦

软件测试开发工程师

[转载]使用 IconHandler 2.0 获取文件图标

mikel阅读(1002)

[转载]使用 IconHandler 2.0 获取文件图标 – 鹤冲天 – 博客园.

在 Windows 资源管理器中,我们可以使用图标方式来查看文件,图标可以让我们快速判断文件的类型,如下图中所选中的文件,相信大家一眼就能看出是一个 Word 文档。

icon1

设计美观的图标让我们赏心悦目,如果可以把这些小图标直接用在我们写的程序中,一定会增色不少。

但 .Net 并没有直接提供给我们一个可以直接获取文件图标的办法,如果使用搜索引擎查找解决办法的话,你会找到很多解决办法,但大多是以零散的代码提供的,质量和来源都没有保证。

我找到一位国外大牛写的 IconHandler,分享给大家!

IconHandler

IconHandler 是由 MVolo 分享的一个类库,目前为 2.0 版本,网址为:

http://mvolo.com/blogs/serverside/archive/2008/04/27/IconHandler-2.0-File-icons-in-ASP.NET-applications.aspx

下载后打开压缩包里面如下包含两个 dll 文件:

IconHandler2

第一个文件 ShellIcons.dll 用于从系统中获取图标。第二个文件 ShellIconHandler.dll 用于在 ASP.NET 显示图标。先看第一个文件。

ShellIcons.dll

获取图标我们只需要调用 ShellIcons.GetIconForFile 静态方法,这个方法有三个参数:

public class ShellIcons
{
    public static IconContainer GetIconForFile(string path, bool useExtension, bool largeIcon);
}

先来看两段代码,这三个参数的含义就自然明白了:

//WinForm:获取 rar 文件的大图标并显示在 pictureBox1 上
using (var container = ShellIcons.GetIconForFile(".rar", true, true))
{
    Icon rarFileLargeIcon = container.Icon;
    Bitmap rarFileLargeBmp = rarFileLargeIcon.ToBitmap();
    pictureBox1.Image = rarFileLargeBmp;
}
//WinForm:WinForm:获取 Fiddler.exe 的小图标并显示在 pictureBox2 上
using (var container = ShellIcons.GetIconForFile(@"C:\Program Files\Fiddler2\Fiddler.exe", false, false))
{
    pictureBox2.Image = container.Icon.ToBitmap();
}

显示如下:

ShellIcons

ShellIconHandler.dll

这个 dll 用在 ASP.NET 中显示图片,我们先看作者文件中的一张图片:

IconHandler-PPTPreview

这个调用酷吧,使用也比较简单,首先引用上面这两个 dll 文件,然后修改 web.config 文件(加入带下划线的部分):

<configuration>
    <!-- ShellIconHandler configuration section declaration -->
     <configSections>
        <section name="iconHandler" type="Mvolo.ShellIcons.Web.ShellIconHandlerConfigurationSection" />
    </configSections>
    <system.webServer>
        <!-- Add IconHandler for IIS 7.0 Integrated mode -->
        <handlers>
            <add name="iconhandler" path="geticon.axd" verb="GET" type="Mvolo.ShellIcons.Web.ShellIconHandler" />
        </handlers>
        <validation validateIntegratedModeConfiguration="false" />
    </system.webServer>
    <system.web>
        <!-- Add IconHandler for IIS 6.0 / IIS 7.0 Classic mode -->
        <httpHandlers>
            <add path="geticon.axd" verb="GET" type="Mvolo.ShellIcons.Web.ShellIconHandler" />
        </httpHandlers>
    </system.web>
    <!--
    Icon Handler by Mike Volodarsky
    Retrieves the shell icon for the specified file name.
    -->
    <iconHandler enabled="true"
            alwaysUseExtension="true"
            enableClientCaching="true"
            enableServerCaching="true" />
</configuration>

OK 了,我们在首页显示几个图标试试:

<img src="geticon.axd?file=.docx&size=small" alt=".docx" />
<img src="geticon.axd?file=.xlsx&size=small" alt=".xlsx" />
<img src="geticon.axd?file=.pptx&size=small" alt=".pptx" />
<br />
<img src="geticon.axd?file=.docx" alt=".docx" />
<img src="geticon.axd?file=.xlsx" alt=".xlsx" />
<img src="geticon.axd?file=.pptx" alt=".pptx" />

显示如下:

IconHandler-view

效果不错吧! (还可以在配置文件中,通过 iconHandler 节的选项来指定是否进行缓存。)

写到这里,也许有的朋友会问,网站通常部署在服务器上,如果服务器上没有安装 Office 这些图标能正确显示吗?很不幸,不能显示,IconHandler 通过 shell API SHGetFileInfo 来获取图标,如果文件类型没有在系统中注册的话,是获取不到的。但庆幸的是 MVolo 考虑到了这个问题,并给我们供了一个解决办法:

在 Web 项目中新建立一个名为 /App_Resources/Icons 的文件夹中,将图片以下面的方式放入:

app_resources_icons

还要再修改下 iconHandler 配置节(加入带下划线的部分):

<configuration>
    <iconHandler enabled="true"
                alwaysUseExtension="true"
                enableClientCaching="true"
                enableServerCaching="true"
                useSavedIcons="true" />
</configuration>

我们再测试一下,在首页中加入以下代码:

<img src="geticon.axd?file=.hctx&size=small" alt=".hctx" />
<img src="geticon.axd?file=.hctx" alt=".hctx" />
<br />

预览如下:

IconHandler-view2

解决了,但实际文件类型可能有好几百甚至上千种,一个个来太麻烦,幸好 MVolo 也为我们提供了另外一个工具。

IconGen.exe

使用这个工具我们可以批量生成文件对应的大小图标,这是一个控件台应用程序,只需要执行以下两个命令:

> IconGen.exe c:\Icons large
> IconGen.exe c:\Icons small

分别生成大小图标,如下:

IconGen

大大小小一共生成了 2038 个图标!

相关下载: IconHandler 2.0 IconGen.exe