2009年4月29日星期三

又一次赶超美帝国主义

http://news.qq.com/a/20090427/000521.htm
《专家称猪流感病毒属异常新型 现阶段无法受控》

美国疾病及预防中心专家日前证实,这次爆发的猪流感病毒是属于异常的新型病毒,
而且散播范围很广,现阶段无法受到控制。(2009年04月27日08:47)

然后,马上,中国专家开始研究…………

http://news.qq.com/a/20090427/000635.htm
《中国疾控专家称人感染猪流感“可防可控可治”》

中国疾病预防控制中心专家26号表示,猪流感并不可怕,
目前虽尚无预防疫苗,但人感染猪流感是可防、可控、可治的。(2009年04月27日09:29)

-----------------------------------------------------------------

早上和LF看新闻的时候,说到祖国就是伟大.科技比美国先进多了.

罪恶感油然而生

看了一上午的乱七八糟的东西,从"猪流感"到将中国划分为50个省,就是没有写代码,也没有做任何上班时该做的事情,有点罪恶感.特发此日志,作为纪念.于是我觉得,我还是看点程序相关的东西吧.

----------------------分割线--------------------

我终于使用live writer连上blogspot了,现在正在使用哦....

2009年4月28日星期二

JST学习笔记

JST(JavaScript Templates),JST引擎使用JavaScript编写,支持类似于FreeMarker等模块标记语言。

例子

首先在html中使用jst,要导入TrimPath JST js库。
<html>
<head>
<script language="javascript" src="trimpath/template.js"></script>
...
</head>
...
</html>
然后使用js创建数据:
<script language="javascript">
var data = {
products : [ { name: "mac", desc: "computer", 
price: 1000, quantity: 100, alert:null },
{ name: "ipod", desc: "music player",
price:  200, quantity: 200, alert:"on sale now!" },
{ name: "cinema display", desc: "screen",   
price:  800, quantity: 300, alert:"best deal!" } ],
customer : { first: "John", last: "Public", level: "gold" }
};
</script>
将jst模板写在一个隐藏的textarea中,如:
<textarea id="cart_jst" style="display:none;">
Hello ${customer.first} ${customer.last}.<br/>
Your shopping cart has ${products.length} item(s):
<table>
<tr><td>Name</td><td>Description</td>
<td>Price</td><td>Quantity & Alert</td></tr>
{for p in products}
<tr><td>${p.name|capitalize}</td><td>${p.desc}</td>
<td>$${p.price}</td><td>${p.quantity} : ${p.alert|default:""|capitalize}</td>
</tr>
{forelse}
<tr><td colspan="4">No products in your cart.</tr>
{/for}
</table>
{if customer.level == "gold"}
We love you!  Please check out our Gold Customer specials!
{else}
Become a Gold Customer by buying more stuff here.
{/if}
</textarea>
要调用结果的时候,使用js调用:
//The one line processing call...
var result = TrimPath.processDOMTemplate("cart_jst", data);
JST API

TrimPath对象

JST组建的所有函数都有TrimPath对象提供,相当于命名空间。
TrimPath.parseDOMTemplate(elementId,optionalDocument)
查找到DOM树中elementId指定的元素,(一般来说, elementId都指向隐藏的textarea,因为textarea具有良好的特性,它的innerHTML可以支持任何html代码,以及JST模板语言中的任何东西),将其innerHTML解析为一个模板对象(template object), optionalDocument可选,如果在iframes中或者多个窗口时,指定某个document。

TrimPath.parseDOMTemplate(elementId, optionalDocument)

查找到DOM树中elementId指定的元素,(一般来说, elementId都指向隐藏的textarea,因为textarea具有良好的特性,它的innerHTML可以支持任何html代码,以及JST模板语言中的任何东西),将其innerHTML解析为一个模板对象(template object), optionalDocument可选,如果在iframes中或者多个窗口时,指定某个document。

TrimPath.processDOMTemplate(elementId,contextObject,optionalFlags,optionalDocument)

该函数,先调用TrimPath.parseDOMTemplate(),然后在返回的模板对象上调用process ()方法,返回一个已经包装好数据的字符串。

参数 意义
elementId DOM树中包含JST模板的元素id
contextObject 包含JST模板待包装的各种相关数据,是一个js对象
optionalFlags 几个可选的标记对象
optionalDocument 可选的document参数,主要用在多ducument环境中

TrimPath.parseTemplate(templateContentStr,optionalTemplateName)

将一个字符串解析成为一个templateObject,如解析出错,则抛出异常。函数参数如下:

参数 意义
templateContentStr JST模板字符串,如"hello ${firstName}"
optionalTemplateName 指定模板的名字(可选),(用来调试)

templateObject.process(contextObject,optionalFlags)

模板对象的process方法将模板和包含内容的"contectObject"的组装在一起,最终返回一个字符串。 contectObject参数必须是一个js对象,如果在模板中引用到"${a}",那么contectObject必有有a这个属性,即 contectObject.a是可访问的,同理,如在模板中有"${a.b.c}",那么contectObject.a.b.c.d也必须是可访问的。 contectObject中可以包含任何js对象,包括字符串,数字,日期,对象和函数等。在模板中"${groupCalender(new Date())}"将会调用contectObject.groupCalender(new Date())。当然contectObject中得有这个函数,而且该函数必须返回一个字符串。
optionalFlags也是一个参数,该参数可选。该参数可以指定解析模板时的一些行为。
标记 意义
optionalFlags.throwExceptions 默认为false;设为true时,如有异常发生,将会抛出异常,异常字符串会接在当前已解析的字符串返回。
optionalFlags.keepWhitespace 默认为false;设为true时,模板中的所有空白字符将被保留。

使用Prototype定义javascript中的类和继承

在较早版本的Prototype框架中,创建类的基本方法是调用Class.create()函数.通过这种方法创建的类,他的构造函数回自动调用initialize方法.从Prototype1.6.0开始,Class模块开始支持继承,相比老版本有较大改进,现在创建新类的时候将更加方便。 在Prototype中创建类的基本方法仍然是Class.create()函数。在新版本中,以前的代码仍能支持;但是现在,我们可以不用直接对JavaScript对象的prototypes 进行修改或者使用Object.extend()来复制所有属性。 例子 先让我们看一下早版本中Prototype中的类的定义和继承。

/** obsolete syntax **/

var Person = Class.create();
Person.prototype = {
initialize: function(name) {
this.name = name;
},
say: function(message) {
return this.name + ': ' + message;
}
};

var guy = new Person('Miro');
guy.say('hi');
// -> "Miro: hi"

var Pirate = Class.create();
// inherit from Person class:
Pirate.prototype = Object.extend(new Person(), {
// redefine the speak method
say: function(message) {
return this.name + ': ' + message + ', yarr!';
}
});

var john = new Pirate('Long John');
john.say('ahoy matey');
// -> "Long John: ahoy matey, yarr!"

这里我们可以看到继承是通过修改类的prototype,并且调用Object.extend函数。同时,Pirate重写了Persion的say()方法,我们没有办法向在编程语言中那样使用基类的方法。 在新版本中都做了改进,下面的代码是使用新版,相比上面的代码,将更简洁。

/** new, preferred syntax **/

// properties are directly passed to `create` method
var Person = Class.create({
initialize: function(name) {
this.name = name;
},
say: function(message) {
return this.name + ': ' + message;
}
});

// when subclassing, specify the class you want to inherit from
var Pirate = Class.create(Person, {
// redefine the speak method
say: function($super, message) {
return $super(message) + ', yarr!';
}
});

var john = new Pirate('Long John');
john.say('ahoy matey');
// -> "Long John: ahoy matey, yarr!"

现在基类和子类的定义都更简洁,因为我们不再需要直接修改对象的prototype。同时还有一个新特性,就是增加了$super关键字,支持对基类方法的调用。 如何组合模块 现在已经知道通常创建类的方法是调用Calss.create()函数.

var Pirate = Class.create(Person, { /* instance methods */ });

但实际上,Class.create函数可以接收任意数量的参数。第一个参数,如果是一个另外定义的类,那么新创建的类将继承它。另外所有的参数都会作为实例方法增加到新创建的类中。这样可以方便的组合已有各个模块。

// define a module
var Vulnerable = {
wound: function(hp) {
this.health -= hp;
if (this.health < 0) this.kill();
},
kill: function() {
this.dead = true;
}
};

// the first argument isn't a class object, so there is no inheritance ...
// simply mix in all the arguments as methods:
var Person = Class.create(Vulnerable, {
initialize: function() {
this.health = 100;
this.dead = false;
}
});

var bruce = new Person;
bruce.wound(55);
bruce.health; //-> 45

上例中Vulnerable不是一个类,但是新定义的Person类仍然包含了所有Vulnerable的方法和属性。Prototype的这个特性,使我们很容易地组合各个模块,提高代码重用率。 方法定义中的$super参数 当我们在子类中重写一个方法,并希望调用基类的方法,那么需要一个指向基类方法的引用。现在可以通过传入一个附加参数$super来指向基类的方法($super参数必须作为第一个参数)。但是外部调用的时候,不必需要知道这个$super参数,如上例中,调用Pirate的say方法时,仍然只需要传入一个参数。

2009年4月25日星期六

2009年4月21日星期二

装13句式二十四

转自http://blog.huangjiwei.com/?p=3245

一定要幸福哦!(简直是装13大忌啊,你要是发自内心的会厚着脸皮说出来吗?)

如果爱,请深爱。

好吧,我承认我……(谁TMD要你承认,自我感觉太良好了吧?)  
那一刻,我泪流满面 。(脆弱的小心灵哟,你如此流泪为哪般?)  
此女子……此男子……(你妈没教过你第一人称咋用么)  
我们都是好孩子 最最善良的孩子……(鸡皮疙瘩掉一地,孩子孩子,你他妈都孩他娘了)  
亲们……(或者“亲爱的们”)

××××得让人心疼  
★ ×)*&……%¥#绘銪兲驶替硪嫒尓(会有天使替我爱你?兲是什么?会有王八替我爱你?) 
亲爱哒……(“的”就“的”么,你“哒”个毛啊?)  
没心没肺地大笑; 没心没肺地+某动词 (你丫装什么纯情?) 
我依然是骄傲的公主~(这年头公主满大街都是)  
希望……带着我的音乐梦想走下去……以及我会带着我最初的音乐梦想走下去(苍天啊,劈死我吧)  
我们是糖,甜到哀伤。  
这时不说这时,说“彼时”也不说也,说“亦”这样不说这样,说“如此” 然而不说然而,说“然”。  
他,无奈而宠溺的看着我,“×××,我到底该把你怎么办才好”(日后再说)  
人生若只如初见(好好的一句话如今被装逼装到恶俗)  
写签名这样的:××(自己的名字)不要哭,××要坚强,××要勇敢,××今天好乖~  
他是(不是)我想要的那杯茶……(小资13)  
我喜欢抬起头成45度角仰望天空,我的泪才不会流那么多……(直接90度不就完了,费个什么劲啊)  
传说每个女生都是天使,为了某个男生所以折断翅膀来到人间,男生一定好好对待这个女生,因为她再也回不到天堂……(明明都是受精卵,装哪门子的天使)  
男子,女子,忧郁,忧伤,態度, 帆布鞋,海藻般的长发,赤裸着××,干净的白衬衫,轻轻的搅动着那一杯卡—布—奇—诺!(最烦卡布奇诺,NND你也就知道个卡布奇诺)  
装13关键词:唯美、尊贵、风情、品牌、强势、高档、奢华、地中海、碧水蓝、简约生活……(滚回家盖房子去吧)
你是我的伤。心上的伤。我很伤……(吃药去吃药去吃药去,瞎嚷嚷个什么)

2009年4月17日星期五

谣言罚款和第四个盛世

http://news.163.com/09/0413/06/56OQ2DM10001124J.html
浙江省杭州市人大常委会决定立法净化网络环境,从今年5月1日起,散布谣言、制作传播病毒者将受到法律的惩处。

浙江省十一届人大常委会第十次会议近日批准的《杭州市计算机信息网络安全保护管理条例》规定,从今年5月1日起,任何单位或者个人不得从事下列危害计算机信息网络安全和秩序的行为,包括擅自进入、使用他人计算机信息网络;擅自增加、修改、删除、干扰他人计算机信息网络的功能;故意制作、传播、使用计算机病毒、恶意软件等破坏性程序,或者制作、发布、复制、传播含破坏性程序或其机理、源程序的信息等。

该条例指出,单位或者个人违反上述规定,由公安机关给予警告,有违法所得的,没收其违法所得;对单位可并处以1000元以上1.5万元以下的罚款,对个人可并处以500元以上5000元以下的罚款;情节严重的给予6个月以内停业整顿、停机联网的处罚。

杭州市人大常委会有关负责人表示,随着网络交流方式的盛行,网民、网吧数量的快速增长,以及宾馆、咖啡馆等提供公共上网服务的场所日益增多,电子商务已经成为经济发展的重要组成部分,计算机信息网络安全问题也日益突出。(岳德亮)

转北风的:

对此,有论者认为:“很多时候,即使在大体真实的舆论中,也会包含有不完全真实的细节。这样一来,立法要是取缔了不完全真实的舆论的时候,也就消灭了基本真实的信息本身。”


呵呵,以后不能随便骂人了.骂人也只能骂骂万恶的资本主义.我朝党国天下正步入继继西汉“文景之治”、唐“贞观之治”、清“康雍乾盛世”之后的“第四个盛世”呢。

2009年4月16日星期四

中国络封锁技术方案与反网络封锁技术方案

转载自左拉的经常被封锁的网站:http://knol.google.com/k/-/-/3jhi1zdzvxj3f/2#

目录

  1. GFW是什么
  2. 网络封锁的技术方案
    • 内容审查
    • 屏蔽IP
    • DNS劫持
  3. 反网络封锁的技术方案
    • 使用SSL加密对抗关键字审查
    • 给网站换IP避开IP屏蔽
    • 用tor来避开DNS干扰
    • 使用在线RSS阅读器来

GFW是什么

GFW是Great Fire Wall的缩写,是金盾工程。这个工程由若干个部分组成,实现不同功能。防火长城主要指中国政府监控和过滤互联网内容的软硬件系统,由服务器和路由器等设备,加上相关的应用程序所构成。

首先,需要强调的是,由于中国网络审查广泛,中国国内含有“不合适”内容的的网站,会受到政府直接的行政干预,被要求自我审查、自我监管,乃至关闭,故GFW的主要作用在于分析和过滤中国境内外网络的资讯互相访问。

GFW对网络内容的过滤和分析是双向的,GFW不仅针对国内读者访问中国境外的网站进行干扰,也干扰国外读者访问主机在中国大陆的网站,本文讨论GFW屏蔽国外网络上传播的内容的方法及相应的对策。

网络封锁的技术方案

GFW在网络上封锁的技术方案有:

  1. 内容审查
  2. 屏蔽IP
  3. DNS劫持

GFW如何屏蔽网络上传播的内容?网络上封锁的具体方式:

  1. 内容审查

    GFW的内容审查针对HTTP传输协议的默认端口的80端口,HTTP传播的内容是明文的内容,没有经过加密,GFW是一个IDS[Intrusion detection system (入侵检测系统)],GFW有一个敏感字名单,若在中国大陆访问境外的主机的HTTP的数据流里发现敏感字眼,就在两台主机间伪造一个"reset”信号,导致双方主机以为对方中止了请求。如用Firefox浏览器访问
    http://newsvote.bbc.co.uk/chinese/
    http://knol.google.com/k/-/-/3jhi1zdzvxj3f/2 就会出现以下画面:
  2. 屏蔽IP

    屏蔽IP是GFW通过路由器(router)来控制的,在通往国外的最后一个网关上加上一条伪造的路由规则,导致通往某些被屏蔽的网站的所有IP数据包无法到达。路由器的正常工作方式是学习别的路由器广播的路由规则,遇到符合已知的IP转发规则的数据包,则按已经规则发送,遇到未知规则IP的数据,则转发到上一级网关。GFW的路由器按黑名单(blacklist)来转发特定的IP数据包,则可屏蔽特定的网站的IP,此IP黑名单不是固定的,会更新。如访问 http://www.dw-world.de/chinese/ 出现以下画面:
  3. DNS劫持

    DNS劫持是针对某些网站的最严重的干扰。干扰的方式有两种:
    1. 一种是通过网络服务提供商(Internet Service Provider/ISP)提供的DNS服务器进行DNS欺骗,当人们访问某个网站时,需要要把域名转换为一个IP地址,DNS服务器负责将域名转换为IP地址,中国大陆的ISP接受通信管理局的屏蔽网站的指令后在DNS服务器里加入某些特定域名的虚假A记录,当使用此DNS服务器的网络用户访问此特定网站时,DNS服务便给出假的IP地址,导致访问网站失败,甚至返回ISP运营商提供的出错页面和广告页面。
    2. 另一种是GFW在DNS查询使用的UDP 53端口上根据黑名单进行过滤,遇到通往国外的使用UDP的53端口进行查询的DNS请求,就返回一个虚假的IP地址
    例子:
    在大陆访问 http://www.zuola.com/ 就可能出现“服务器响应时间过长”。
    我家目前用的中国电信提供的网络接入:
    Internet connection
    Connection type:PPPoE
    IP Address:220.168.9.112
    Gateway address:220.168.8.1
    DNS Server:222.246.129.80, 59.51.78.210
    当我使用中国电信提供的DNS服务器进行查询时,出现以下画面:
    甚至我在中国大陆使用用国外的OPENNDS或其他DNS服务器进行查询,都会被GFW干扰而得到虚假的IP地址,
    通过远程登录在美国的主机进行DNS查询后,此时的DNS查询是完全无GFW干扰的,可得到 www.zuola.com的真实IP地址是 75.119.214.237

GFW在OSI结构模型的两个层面进行审查和封锁,一种在传输层(Transport Layer)进行干扰,一种是在网络层(Network Layer)进行干扰:

  1. 在传输层(Transport Layer)进行干扰:
    1. DNS劫持 GFW在UDP 53端口上进行传输层完成干扰;
    2. 内容审查 GFW对默认端口TCP 80端口上进行内容过滤,在http传输协议上,对tcp 80端口上传输的内容进行内容审查,遇到关键字,GFW就在会话中插入“reset”信号,导致网页被重置。
  2. 屏蔽IP是GFW在网络层(Network Layer)上的封锁和干扰,是在IP路由协议上完成干扰。


GFW审查在OSI模型上的审查的位置:


反网络封锁的技术方案

GFW针对具体网站的方案有以下三种:

  1. GFW把域名为作敏感字来屏蔽
  2. GFW把域名所在的IP屏蔽
  3. GFW干扰域名DNS解析

相应对抗GFW,网站的主人有下面几个方法:

  1. 网站使用SSL加密对抗关键字审查;
  2. 给网站换IP避开IP屏蔽;
  3. 推荐大陆的网站访问者使用国外的DNS解析服务器,或推荐大陆的访问者使用tor来避开DNS干扰
  4. 推荐读者使用在线RSS阅读器来读取最新文章

相应对抗GFW有的具体方法

  1. 使用SSL加密对抗关键字审查

    HTTPS是基于SSl的HTTP协议,是安全超文本传输协议。当用户访问使用HTTPS的网站时,用户端的浏览器需要先获取网站的安全证书和来自认证的安全证书发行商的数字签名,此安全证书为服务器的公钥,服务器用私钥把网页内容加密进行传输,客户端浏览器以公钥解密即可得到网页内容。网络内容的传播过程中,内容是加密的,无法被GFW自动审查传输的内容,亦无法被GFW插入"reset"信号。
    1. 实例1:在2007年11月到2008年7月,https://www.zuola.com 曾用HTTPS成功避开GFW的审查,让中国大陆的网民能够直接访问此网站。
    2. 实例2:当人们无法访问http://knol.google.com/k/-/-/3jhi1zdzvxj3f/2时,改用 https://knol.google.com/k/-/-/3jhi1zdzvxj3f/2 就可以访问
  2. 给网站换IP避开IP屏蔽

    使用SSL加密可对抗GFW的内容审查,GFW如何对付HTTPS呢?
    GFW的工作人员遇到使用HTTPS的网站后,他们知道无法使用机器自动的过滤他们心目中的“敏感内容”和“有害内容”,于是直接屏蔽网站的IP,此时,通往此网站服务器的任何数据包都无法直接越过GFW到达目的地。此时只需要更改网站域名的A记录,换一个IP就可以避开屏蔽IP。此时就像猫和老鼠的游戏了,GFW只好跟在后面不断屏蔽IP,并且GFW的屏蔽IP的工作是人工执行的。无法通过机器自动执行。如果GFW能够自动执行屏蔽IP的操作,那被屏蔽IP的域名的拥有者就可以借用GFW的能力屏蔽任何境外网站了。
    1. 实例3:在2007年11月到2008年7月,https://www.zuola.com 曾用SSL加密+换IP 成功避开GFW的审查,让中国大陆的网民能继续能够直接访问此网站。
    2. 实例4:https://doubleaf.com 也使用SSL加密和换IP的手段让中国大陆的网民能直接访问
  3. 推荐大陆的网站访问者使用国外的DNS解析服务器,或推荐大陆的访问者使用tor来避开DNS干扰

    在2008年奥运期间,我发现GFW针对 www.zuola.com 使用了DNS劫持的方式一劳永逸的屏蔽方式,导致国内用户无法直接获得我的网站的正确IP,即使使用“无界”“自由门”“VPN”等突破封锁的工具,也会走错门。那就只能推荐读者《使用TOR+FoxyProxy插件突破GFW》了,TOR是一个分布式的、匿名的网络,Foxyproxy是一款代理服务器管理软件,可以让Firefox使用TOR来收发DNS请求:



    TOR的工作方式
    Tor 有助于降低简单的和高级的流量分析的风险,Tor 把你的流量分散到互联网上的多个地点,所以不存在单一的一点可以把你和你的目的地联系起来。这就好像用一条拐弯抹角的、难以辨认的路径甩掉跟踪你的人,然后定期擦掉你的脚印。在 Tor 网络上,来源和目的地不是用一条路径直接连接的,而是由一条通过数台中继的随机路径覆盖原始路径,数据包在这条路径上传输,因此,不存在任何单一一点上的观察者能够知道数据从哪里来、到哪里去。


    用 Tor 创建一条私有网络路径时,用户的软件或客户端通过网络上的中继递增地建立一条由若干加密连接组成的环路(circuit)。环路一次扩展一跳(hop),环路上的中继仅仅知道它从哪一个中继接收数据以及向哪一个中继发送数据。没有一台单独的中继会知道数据包的完整路径。客户端与环路上的每一跳都协商一组独立的密钥,这样可以保证数据通过任何一跳时都无法跟踪。


    一旦环路建立完成,多种类型的数据可以在上面进行交换,不同种类的应用程序也可以在 Tor 网络上部署。因为每一台中继最多只能知道环路中的一跳,窃听者或者被入侵的中继都无法通过流量分析把连接的来源和目的地联系起来。 Tor 仅作用于 TCP 数据流,任何支持 SOCKS 的应用程序都可以使用它。

    出于有效性,Tor 为大约在相同的十分钟内发起的连接请求分配同一环路。以后的请求被分配不同的环路,这样他人就不能把你早先的行为和新的行为联系起来。

  4. 推荐读者使用在线RSS阅读器来读取最新文章

    Really Simple Syndication(RSS)是一种严格XML的信息传递的格式规范,标准的XML格式文档可允许信息在一次发布后通过不同的程序阅读,易于分发和聚合(Sysndicat)。RSS是这种XML应用搭建了信息迅速传播的一个技术平台,网站的最新内容通过RSS Feed传播,能通过在线RSS阅读器自动推送文章到读者的阅读器,人们无需直接访问网站就能看到最新的内容,RSS能够让内容很容易的被分发,只需要知道RSS地址,网络上无数的在线RSS阅读器总能读取到RSS Feed中的内容。RSS是很容易生成并分布的,这是GFW无法拦截或需要大量资源才能够封锁的一种技术。
    实例:
    1. 实例5:我的网站自2007年4月重庆最牛钉子户报道后被屏蔽,但我的FEED地址 http://feed.zuola.com/ 的订阅数却一直在增长,Google Reader订阅用户数量从 700左右增加到2686。不过GFW仍然会干扰Google reader,使用https 的方式https://www.google.com/reader/view/ 才能看到以下数据
    2. 实例6:《看不见的西藏》是一个藏族人的BLOG,虽然国内读者无法直接访问,但中国大陆的读者仍然能够通过订阅她的RSS FEED来看到她眼中的西藏,这个Feed是可用google reader订阅的 http://woeser.middle-way.net/feeds/posts/default




此文档的其他站点备份:

  1. http://zolaruler.spaces.live.com/blog/cns!D2AC7D299F493A68!241.entry
  2. https://docs.google.com/View?docid=dggh5mp6_0zzmm4fdn
  3. http://zhoushuguang.blogspot.com/2009/03/blog-post.html
  4. https://knol.google.com/k/-/-/3jhi1zdzvxj3f/14

参考文档:

Tor概述》:http://tor.zuo.la/overview.html.zh-cn

手把手教你使用TOR+FoxyProxy突破GFWhttps://knol.google.com/k/-/-/3jhi1zdzvxj3f/2

纠正网友一些对GFW的认识》: https://www.zuola.com/weblog/?p=1228

Zola教你玩:如何对抗GFW的域名劫持》: https://www.zuola.com/weblog/?p=1151

2009年4月15日星期三

FreeMarker中文手册--KLW 表达式


本页内容:

  • 快速浏览(备忘单)

  • 直接赋值

  • 取值

  • 字符串操作

  • 序列(数组)操作

  • 哈希操作

  • 算术运算

  • 比较运算

  • 逻辑操作

  • 方法调用

  • 处理缺失值

  • 括号

  • 表达式中的空白

  • 操作符优先级


当你给内插表达式或者指令参数提供值的时候,你可以使用变量或者更复杂的表达式.例如,x是数值8,y是数值5, (x + y)/2的值会是6.5.

在我们进入具体细节之前,先看一些具体的例子:

  • 当你为内插表达式提供值时:使用方法是${表达式}.这里的表达式是你要在文本输出中的值.如${(5 + 8)/2} 会打印”6.5”.(也有可能是是”6,5”,如果你输出的语言环境不是英语的话.)

  • 当你给指令提供参数时:你已经在入门章节见过if指令.这个指令的语法是: <#if 表达式>...</#if>.这里的表达式必须是一个布尔值.例如: <#if 2 < 3>,2 < 3(2小于3)是一个布尔表达式(结果为true).


快速浏览(备忘单)

  • 直接赋值


    • 字符串: "Foo" 或者 'Foo' 或者"It's \"quoted\"" 或者r"C:\raw\string"

    • 数值: 123.45

    • 布尔值: true, false

    • 序列(数组): ["foo", "bar", 123.45], 1..100

    • 哈希: {"name":"green mouse", "price":150}


  • 取值


    • 顶层变量: user

    • 从哈希中取值: user.name, user["name"]

    • 从序列中取值: products[5]

    • 特定变量: .main


  • 字符串操作


    • 内插表达式 (或者连接操作): "Hello ${user}!" (or "Free" + "Marker")

    • 取得一个字符: name[0]


  • 序列操作

    • 连接: users + ["guest"]

    • 序列片段: products[10..19] 或 products[5..]

  • 哈希操作

    • 连接: passwords + {"joe":"secret42"}

  • 算术运算: (x * 1.5 + 10) / 2 - y % 100

  • 比较: x == y, x != y, x < y, x > y, x >= y, x <= y, x < y, ...等.

  • 逻辑运算: !registered && (firstVisit || fromEurope)

  • 内置函数: name?upper_case

  • 方法调用: repeat("What", 3)

  • 缺失值处理:

    • 默认值: name!"unknown" 或(user.name)!"unknown" 或name! 或(user.name)!

    • 测试值是否缺失: name?? 或(user.name)??



直接赋值
经常你会直接使用具体值,而不是使用一些计算结果.
字符串
直接使用字符串的话,你在文本上加引号即可,如"some text",或者使用单引号,如 'some text'.这两种形式是等价的.如果字符串本身包含着引号(“或者’)或者反斜杠”\”,你需要加上前置的反斜杠,这称为转义.其他的字符你都可以直接输入,包括换行符.例如:

${"It's \"quoted\" and
this is a backslash: \\"}

${'It\'s "quoted" and
this is a backslash: \\'}

输出结果:

It's "quoted" and
this is a backslash: \

It's "quoted" and
this is a backslash: \

注意
当然,你也直接将文本写在模版中,而不使用 ${...}.这里只是作为例子展示表达式中如何直接使用字符串.

下表是所有支持的转移字符.在模版中使用反斜杠来转义其它字符会导致模版出错.

转义字符意义
\"双引号(u0022)
\'单引号 (u0027)
\\反斜杠 (u005C)
\n换行符 (u000A)
\r回车符 (u000D)
\t水平制表符(u0009)
\b退格符 (u0008)
\f换页符(u000C)
\l小于号: <
\g大于号: >
\a&
\xCode使用16进制unicode表示符号


\x之后的code是1到4位的16进制数字.例如,在字符串中表示一个版权信息: "\xA9 1999-2001", "\x0A9 1999-2001", "\x00A9 1999-2001".你必须使用1到4位的16进制数字,否则FreeMarker会很迷茫的,呵呵.

注意到字符”${”和”#{”有特殊意义,它是用来插入值或者表达式的,(如"Hello ${user}!",变量user的值会插入到字符串中.)如果你想打印${,你需要使用原始字符串.

所谓原始字符串,指的是,在原始字符串何总反斜杠和${没有特别意义,会被当做普通文本输出.指明一个字符串是一个原始字符串,在引号前加字母r,例如:

${r"${foo}"}
${r"C:\foo\bar"}

输出:

${foo}
C:\foo\bar

FreeMarker中文手册--KLW 指令


指令
你使用FTL标签来调用指令.在例子中你调用了list指令,语法上,你使用了两个标签: <#list animals as being> 和 </#list>.

一共有两种标签:
  • 开始标签: <#directivename parameters>
  • 结束标签: </#directivename>
这和HTMl或者XML的语法类似,除了标签都是#开始的.如果标签没有嵌套内容(位于开始标签和结束标签之间的内容),你必须只使用开始标签而不是结束标签.例如你可以写 <#if something>...</#if>,但是include指令,只能这么写 <#include something>.因为FreeMarker知道include指令没有嵌套内容.

参数parameters 的格式有指令名directivename决定.

实际上有两类指令:预定义指令和用户定义指令.对于用户定义的指令,要使用@而不是#,比如<@mydirective parameters>...</@mydirective>.另外不同的是如果用户自定义的指令没有内嵌内容的话,必须这样使用: <@mydirective parameters />,类似于XML中<img ... />.但是用户自定义指令又是一个高级主题,详细见这里.

FTL标签,和HTML标签一样,必须被合适地嵌套.下面的代码是错误的.因为if指令既在内嵌的list指令中,又在它的外边:
<ul>
<#list animals as being>
<li>${being.name} for ${being.price} Euros
<#if user == "Big Joe">
(except for you)
</#list> <#-- WRONG! The "if" has to be closed first. -->
</#if>
</ul>
注意FreeMarker并不关心那些内嵌的HTML标签,只关心内嵌的FTL标签.它把HTML当做普通文本对待,不做任何翻译.

如果你使用一个不存在的指令(比如,你不小心把指令名字打错了),FreeMarker会停止翻译模版,并报告一个错误消息.

FreeMarker会忽略FTL标签中多余的空格.所以,下面这么写也是可以的:
<#list[BR]
animals as[BR]
being[BR]
>[BR]
${being.name} for ${being.price} Euros[BR]
</#list >
但是,无论如何,你不可以在<或者</和指令名之间加入空白,这是不允许的.

关于所有指令的列表可以看指令参考.(但是我建议你先看完本章关于表达式的介绍).

注意
FreeMarker也可以配置成在使用FTL标签和注释时使用[和]而不是<和>,像[#if user == "Big Joe"]...[/#if].更多细节见:其他/另一种语法.

注意
FreeMarker也可以配置为在使用预设指令时不使用#,像<if user == "Big Joe">...</if>.但是,我们不建议你使用这种模式.更多信息见:参考/弃用的FTL系统/老的FTL语法

FreeMarker中文手册--KLW 总体结构


总体结构

模版实际上是你用FTL(FreeMarker 模版语言)编写的程序.这是一个简单的编程语言,仅被设计用来实现模版的,而不是其他复杂的东西.

一个模版(FTL程序)由以下几部分构成:
  • 文本: 文本会被原样输出.
  • 内插值:这部分在输出的时候会被实际计算的值代替.内差值由${和}包围(或者#{和}).
  • FTL 标签:FTL标签和HTML标签类似,但他们实际上FreeMarker的指令,不会被打印到输出中.
  • 注释:这里的注视也和HTML中的注视类似,但是他们是使用 <#-- 和 -->包围的.FreeMarker会忽略注视,不会被写到输出中.
让我们看一个具体的模版,[BR]被加进入,用来表示换行.
<html>[BR]
<head>[BR]
<title>Welcome!</title>[BR]
</head>[BR]
<body>[BR]
<#-- Greet the user with his/her name -->[BR]
<h1>Welcome ${user}!</h1>[BR]
<p>We have these animals:[BR]
<ul>[BR]
<#list animals as being>[BR]
<li>${being.name} for ${being.price} Euros[BR]
</#list>[BR]
</ul>[BR]
</body>[BR]
</html>
FTL区分字母大小写.所以list是一个正确的指令,而List不是.同样的,{name}不同于{Name}或者{NAME}.

注意在内插值仅可以直接使用在文本中,以及字符串表达式中.

一个FTL标签不能内嵌在另一个FTL标签中,或者使用在内差值当中.例如,下面的表达是错误的: <#if <#include 'foo'>='bar'>...</#if>

注释可以放在FTL标签和内差值内部.例如:
<h1>Welcome ${user <#-- The name of user -->}!</h1>[BR]
<p>We have these animals:[BR]
<ul>[BR]
<#list <#-- some comment... --> animals as <#-- again... --> being>[BR]
...
注意

你们如果尝试过上述例子,你也许注意到,一些空格,制表符和换行符,在输出的时候会丢失,尽管我们说文本会被原样输出.现在先别为这个操心,这是因为” white-space stripping”(空白-剥离)特性被设置的缘故,这个特性会自动移除多余的空格,制表符和换行符.

FreeMarker中文手册--KLW 第二章 值和类型

本章内容:注意
这里假设你已经阅读了之前的入门章节.理解值和类型的概念是理解数据模型的关键.但是,值和类型的概念并不局限于数据模型.

FreeMarker中文手册--KLW 第一章 入门


本章内容:本章将对FreeMarker进行非常粗略的介绍。随后的章节将讲述更多的细节。尽管如此,一旦你阅读完本章,你仍能写一些简单但是有用的FreeMarker模版。

FreeMarker中文手册--KLW 第三章 模版

第三章 模版

本章介绍注意
这里假设你已经阅读了入门章节和第二章值和类型.

2009年4月14日星期二

师兄的哥哥上书本了


王德明,原来是我师兄的哥哥.我师兄复姓王德,单名一个庆字.

FreeMarker中文手册--KLW 类型

类型

支持的类型如下:标量(基本类型)

这些是基本简单的值,可以是:
  • 字符串,简单的文本,比如货物的名称.如果你想在模版中直接使用字符串,而不是一个来自数据模型的变量,可以再字符串两端加引号,如"green mouse" 或者'green mouse'.

  • 数值类型:想货物的价格就是数值类型.全部是数字或者非全部数字并没有明显区别.数值类型只有一种.比如,3/2会一直是1.5,而永远不会是1,就好像你在计算器一样.如果你想在模版中使用数字,直接写就行了,如150,-90.06或者0.001.

  • 布尔型:一个布尔型的值代表逻辑上的真(true)或假(false).例如,一个访问者是否登录.通常你会在if指令中使用布尔值作为条件,像<#if loggedIn>…<#/if>或者 <#if price == 0>...</#if>;其中price == 0部分的值是一个布尔型值.在模版中你可以直接使用保留字true和false代表布尔值.

  • 日期:日期型变量保存着日期时间有关的值.一共有三种:
    • 包含日期,如April 4,2003
    • 包含时间,没有日期部分,如10:19:18 PM,时间部分存为毫秒值.
    • 日期时间(包含日期和时间),如 April 4, 2003 10:19:18 PM,时间部分存为毫秒值.
    不幸的是,由于java平台的限制,FreeMarker有时不能区分是哪部分在使用(日期还是时间).针对这个问题的解决办法是一个高级主题,这里暂不讨论,详细见这里.直接在模版中定义日期值是可以的,但这也是高级主题,详细见这里.
另外需要记住FreeMarker是区分字符串,数值型和布尔值的,所以字符串”150”和数值150是完全不同的.一个数值型包含数字变量,而布尔型只包含true或false.一个字符串包含由任意个字符组成的序列.

容器

有些变量的目的就是包含其他变量,这些变量就是容器.这些被包含的变量也被称为子变量.容器类型是:
  • 哈希(哈希表):每个子变量都和一个独一无二的名字相关联.在一个哈希表中的所有子变量是无序的,所以对哈希表来说,没有第一个子变量,第二个子变量这种说法.自变量都是根据名字访问的.

  • 序列:每个子变量都和一个整数(索引)相关联.第一个子变量关联的是0,第二个是1,第三个是3,依此类推.所有的子变量是按顺序排列的.这些关联的整数也被称为索引.序列通常是密集的,所有的索引通常都关联着子变量,但这并不严格要求.子变量的类型不要求相同.

  • 集合:一个集合,从模版作者的角度看,是一个受限制的序列.你不同访问它的大小,或者根据索引访问它的子变量.但是这些子变量仍可通过list指令罗列出来.
注意到,一个值可有有多种类型,有可能一个值既是哈希表也是序列,在这种情况下,它既支持基于索引的访问,又支持根据名字的访问.尽管如此,一个容器要吗是一个序列,要么是一个哈希表,而不是两者都是.

因为存在哈希表和序列(和集合)中的变量的值可以是任何东西,它可以是哈希表或者序列或者集合.这样,你可以构造任意深度的数据模型结构.

数据模型本身(或者更确切地说,它的根)就是一个哈希表.

子程序
方法和函数
一个值是方法或者函数,表示它能根据你传入的参数,计算出另外的值.

给编程人员:方法或函数是第一阶层的值,就像函数式编程语言中那样.这表示,方法或函数可以作为参数或其它方法或函数的返回值.你可以使用它们给变量赋值等.

假设编程人员在数据模型中添加了方法变量avg,来计算数值的平均数.如果你使用3和5作参数访问avg,然后你可以得到一个值4.

方法的使用方法稍后再做解释,但是这个例子也许可以帮你理解什么是方法:

The average of 3 and 5 is: ${avg(3, 5)}
The average of 6 and 10 and 20 is: ${avg(6, 10, 20)}
The average of the price of a python and an elephant is:
${avg(animals.python.price, animals.elephant.price)}
输出结果是:
The average of 3 and 5 is: 4
The average of 6 and 10 and 20 is: 12
The average of the price of a python and an elephant is:
4999.5
方法和函数的区别是什么呢?对模版设计者来说,它们并没有区别.实际上并不是没有区别,方法基本上来自数据模型(反应为java对象的方法),而函数是在模版中定义的(使用function指令定义的),但是两者的调用方法是一样的.

用户自定义指令

一个类型为用户自定义指令的值可以作为指令来使用(换句话说,和其他FreeMarker标签一样).一个用户自定义的指令,是一个子程序,比如一些可重用的模版碎片.(又是一个高级主题,以后在说啦).

给编程人员的提示:用户定义的指令,比如宏,也是第一阶层的值,和方法和函数一样.

仅仅是为了理解一下用户自定义指令(如果你不理解直接跳过也行),假设我们有个变量,box,它的值是用户自定义指令,用来打印一些花俏的HTML消息框带标题栏和消息.这个box变量可以这样用在模版中:
<@box title="Attention!">
Too much copy-pasting may leads to
maintenance headaches.
</@box>
方法/函数和用户自定义指令比较

这里又是给高级用户的(如果你不理解,直接跳过).这经常是个两难的决定,当你实现某样东西时,是使用方法函数呢,还是使用用户自定义指令呢.这里的经验法则是:使用用户自定义指令而不是方法或者函数,如果:
  • …输出(返回值)是有标记的(HTML,XML等).主要原因是函数的返回结果会自动过滤XML,而用户自定义指令不会. 用户自定义指令的输出是假设为有标记的.

  • …如果副作用而不是返回结果是重要的.例如,一个向服务日志添加一条记录的指令.实际上,用户定义指令是没有返回结果的.当然通过设置非本地变量,也可以得到一些返回值.

  • …它会做流控制(如list指令和if指令),你在方法和函数中做不到这样.
Java对象的方法在模版中作为方法可见,无论这个java方法本身是否可见.这里,你没得选择.

其他
节点
节点变量代表树结构中的一个节点,在XML处理的时候,会常常用到.这是一个高级并且专门的主题.(呵呵,如果不理解,跳过吧)

不过,给高级用户的一个快速浏览:一个节点类似于一个包含其他节点中的序列,这些包含的序列也被称为子节点.一个节点包还一个它自身容器的引用,也被称为父节点.一个节点的主要信息是它的拓扑信息.另外的数据使用使用一个变量有多种类型这个特性来保存.比如,一个值可以是一个节点,又是一个数值,这样可以讲数值存为”有效负荷”.除了拓扑信息,节点也可以保存一些元信息:节点名,节点类型(字符串等)和节点命名空间.例如,XHML文档中的h1节点,它的名字可以是”h1”,节点类型可以是”element”,它的命名空间可以是” http://www.w3.org/1999/xhtml”.这些元数据代表什么,这是数据模型设计者的事情.如何获得节点的拓扑信息和元信息随后章节会详细描述.(在这里你不理解没有关系.)

FreeMarker中文手册--KLW 基本概念

基本概念

什么是值?
编程人员可以跳过本部分.
首先举一些样例值,如你日常数学中的那些数字,16,0.5等.在计算机语言中值的概念更宽一点,值不一定要是数字.例如,在下面的例子中:

(root)
|
+- user = "Big Joe"
|
+- today = Jul 6, 2007
|
+- todayHoliday = false
|
+- lotteryNumbers
| |
| +- (1st) = 20
| |
| +- (2st) = 14
| |
| +- (3rd) = 42
| |
| +- (4th) = 8
| |
| +- (5th) = 15
|
+- cargo
|
+- name = "coal"
|
+- weight = 40

我们说user变量的值是”Big Joe”(一个字符串).变量today的值是 Jul 6,2007(一个日期),变量todayHoliday 的值是false(一个布尔值),变量lotteryNumbers 的值是一个包含20,14,42,8,15的序列.当然也可以说lotteryNumbers包含多个值(例如,它的第二个值是14), 但是lotteryNumbers本省是一个值.这就像一个盒子里可以包含许多其他东西,但是盒子本省仍然是一个东西.最后我们还有一个cargo变量,它是一个哈希表(又像是一个盒子).所以说,值是一些能存储在变量(如user,cargo,cargo.name)中的东西.但是一个值并不需要存在变量中才可以称为值,例如,我们这里有一个值100(可以直接使用):

<#if cargo.weight < 100>Light cargo</#if>

作为计算的临时结果,也称为值,像20,120(当这个模版执行时会计算出的临时结果).该模版会打印120:

${cargo.weight / 2 + 100}

最后解释一点:作为两个值相除的结果,40(cargo.weight)和2,一个新的值20会被创建.然后再加上100,120这个值也会被创建.然后120会被打印,模版会继续执行,这些临时值被丢弃.

现在你大概知道值的意义了吧.

什么是类型

值的一个重要方面就是他们的类型.例如变量user的值类型就是字符串, lotteryNumbers 的值类型是序列.值的类型是重要的,因为她大致确定了你该如何使用该值.比如${user/2}就是一个错误,但是${cargo.weight / 2}是正确的,并且能打印20.因为除法仅对数字有意义,而对字符串无效.另外,使用小数点的时候,像cargo.name,仅当cargo是一个哈希表的时候才有意义.还有,你使用<#list ...> 指令也仅对序列才有意义. <#if ...>指令的条件值类型必须是布尔型.

一个值可以同时有多种类型,尽管这不常见.例如下面的数据模型,mouse是一个字符串,同时是一个哈希:

(root)
|
+- mouse = "Yerri"
|
+- age = 12
|
+- color = "brown"

如果你将上面的数据模型整合到这个模版中:

${mouse} <#-- uses mouse as a string -->
${mouse.age} <#-- uses mouse as a hash -->
${mouse.color} <#-- uses mouse as a hash -->

输出结果会是:

Yerri
12
brown


数据模型是一个哈希表

从上面几个数据模型的例子中,你已经知道:标记为”root”的只是一个哈希表.当你使用变量user的时候,表示,user变量是存在root(根)哈希中的,就好像你写的是root.user一样.但是实际是没有变量root的.

也许对之前的数据模型你会感到困惑,根哈希表中又包含着另外的哈希表和序列(lotteryNumbers 和cargo).这没有什么特别的.一个哈希表可以包含其他变量.而这些变量包含值,这些值可以是字符串,数字等等.当然这些值也可以是哈希表或者序列.正如之前解释的那样,一个哈希表或者序列也是一个值,和字符串或者数字一样.

2009年4月13日星期一

FreeMarker中文手册--KLW 模版初览


最简单的模版就是纯HTML文件(或者其他任何文本文件,FreeMarker并不局限于HTML)。当客户端访问这个页面时,FreeMarker会发送这个html到客户端。当然你希望页面是更加动态时,你把FreeMarker能理解的部分加入到HTML中:
  • ${...}:FreeMarker在输出中会用实际值替换大括号中的表达式。这些叫interpolation内插值。见第一个例子
  • FTL 标签(FreeMarker 模版语言标签):FTL标签和HTML标签类似,但是这些标签只是FreeMarker的指令,不会在结果中输出。这些标签都由“#”开始(用户自定的FTL标签由“@”开始,而不是”#”,但是这些是高级主题,见后续翻译)。
  • 注释:模版注释和html注释类似,但是是由<#-- 和 -->标记。这两个界定符之间的任何东西包括界定符本身都会被FreeMarker忽略,不会在结果中输出。

除内插值、FTL标签和注释之外的任何值,都被认定为静态文本,不会被FreeMarker翻译,按原样输出。

FTL标签涉及到所谓的指令。这里的关系如同HTML标签(例如:和<table></table>)和HTML元素(如table元素)之间的关系。(如果你没有感觉到这里的不同,尽管将FTL标签和指令当做同义词吧,没有关系)

指令举例

尽管FreeMarker包含很多指令,但是在这个初步预览里,我们只举三个最常用的指令作为例子。

if 指令

使用if指令,我们可以根据条件跳过模版的某一部分。例如在第一个例子当中,我们想向你的老板Big Joe打招呼,而不是其它用户:

<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>
Welcome ${user}<#if user == "Big Joe">, our beloved leader</#if>!
</h1>
<p>Our latest product:
<a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>

这里你告诉FreeMarker,字符串", our beloved leader"仅当变量user的值为"Big Joe"时才会被输出。总的来说,<#if condition>和</#if>之间的内容会被跳过,如果condition值为假的话。

让我们详细地看一下这里的条件:user == "Big Joe"。==是测试左右两边是否相等的操作符,运算结果是一个布尔值,true或者false。在==的左边,我们引用了一个变量,比较时会使用实际值代替。总的来说,在指令或者内插值当中没有引号标记的词FreeMarker都会当做变量来使用。在==的右边,我使用了一个字符串,字符串在模版中必须用引号标记。

如下: 如果price为0的话,会打印"Pythons are free today!":

<#if animals.python.price == 0>
Pythons are free today!
</#if>

类似于刚才直接使用字符串,这里的数字也可以直接使用,注意数字不需要加引号,如果你使用”0”,FreeMarker会将它理解为字符串.

如下: 如果price非0的话,会打印"Python are not free today!".

<#if animals.python.price != 0>
Pythons are not free today!
</#if>

你也可以这样写(使用数据模型初览中的例子):

<#if animals.python.price < animals.elephant.price>
Pythons are cheaper than elephants today.
</#if>

使用<#else>标签,我们指定当condition为假时该做什么.例如:

<#if animals.python.price < animals.elephant.price>
Pythons are cheaper than elephants today.
<#else>
Pythons are not cheaper than elephants today.
</#if>

这个会打印”Pythons are cheaper than elephants today.” 如果python的价格小于elephant的价格,反之则打印”Pythons are not cheaper than elephants today.”

如果你有个包含布尔值的变量(true或者false),你可以直接根在if指令后使用:

<#if animals.python.protected>
Warning! Pythons are protected animals!
</#if>

list 指令

当你想罗列某样东西的时候,list指令变的非常有用.例如,将之前演示序列(数组)的数据模型合并在一个模版中.

<p>We have these animals:
<table border=1>
<tr><th>Name<th>Price
<#list animals as being>
<tr><td>${being.name}<td>${being.price} Euros
</#list>
</table>

输出会是:

<p>We have these animals:
<table border=1>
<tr><th>Name<th>Price
<tr><td>mouse<td>50 Euros
<tr><td>elephant<td>5000 Euros
<tr><td>python<td>4999 Euros
</table>

List指令的通用格式是:

<#list sequence as loopVariable>repeatThis</#list>

repeatThis部分对给定序列的每一项都会重复一次,从第一项开始,一个接着一个.在所有的循环中,loopVariable指向循环的当前值.这个值只存在于<#list …>和<#/list>标签之间(作用域在这两者之间,之外则不可见).

作为另一个例子,我们列出数据模型例子中的所有fruit:

<p>And BTW we have these fruits:
<ul>
<#list whatnot.fruits as fruit>
<li>${fruit}
</#list>
<ul>

你应该熟悉whatnot.fruits这种表达式,见之前数据模型的例子.

include 指令

使用include指令你可以将另一个文件内容插入到模版当中.

假设你要在多个页面下显示版权信息.你可以创建一个仅包含版权信息的文件,然后将该文件插入到任何你需要的地方.如,你将版权信息保存在文件copyright_footer.html中:

<hr>
<i>
Copyright (c) 2000 <a href="http://www.acmee.com">Acmee Inc</a>,
<br>
All Rights Reserved.
</i>

任何时候,你需要这个文件时,仅简单使用include指令即可:

<html>
<head>
<title>Test page</title>
</head>
<body>
<h1>Test page</h1>
<p>Blah blah...
<#include "/copyright_footer.html">
</body>
</html>

输出会是:

<html>
<head>
<title>Test page</title>
</head>
<body>
<h1>Test page</h1>
<p>Blah blah...
<hr>
<i>
Copyright (c) 2000 <a href="http://www.acmee.com">Acmee Inc</a>,
<br>
All Rights Reserved.
</i>
</body>
</html>

如果你更新了copyright_footer.html,访问者会在所有页面看到新的版权信息.

使用多个指令

你可以在页面中任意多次地使用指令,并且你可以切套地使用指令,正如你可以在HTML标签中切套地使用其他HTML标签.例如下例会列出所有的animal,并使用粗体打印他们的名字:

<p>We have these animals:
<table border=1>
<tr><th>Name<th>Price
<#list animals as being>
<tr>
<td>
<#if being.size == "large"><font size="+1"></#if>
${being.name}
<#if being.size == "large"></font></#if>
<td>${being.price} Euros
</#list>
</table>

注意这里,因为FreeMarker不对FTL标签,内插值和注释之外的文本翻译,它不会看到上面内嵌的font标签.

处理缺失变量

在实践中,数据模型经常有一些可选的变量(例如,有时候就是未定义).为防止典型的人为错误,FreeMarker不容忍对缺失变量的引用,除非你明确指明如果变量缺失时该做何处理.这里我们会展示两种最典型的处理方法.

给编程人员的提示:一个不存在(未定义)的变量和一个变量但是包含null值,对FreeMarker来说是一样的.所以这里”缺失”一词包含了这两种情况.

无论何时,我们引用一个变量时,我们都可以指定一个默认值,当这个变量缺失时,使用的时候在变量后跟”!”和默认值.如下例中,当user在数据模型中缺失时,模版会使用字符串”Anonymous”代替user.(当user在数据模型中有定义且不为null时,模版会只用准确的值,而”Anonymous”好像不曾在那里一样):

<h1>Welcome ${user!"Anonymous"}!</h1>

你可以在变量名后跟两个问号??来检测变量是否缺失.结合已介绍的if指令你可以跳过整个问候如果user变量缺失的话:

<#if user??><h1>Welcome ${user}!</h1></#if>

对于多步访问的变量,如animals.python.price,animals.python.price!0仅当animals.python都不缺失并且只有最后一个子变量price,可能缺失(在这种情况下值为0)的时候才是正确的.如果animals或者python缺失,模版处理过程会因”未定义变量”错误而停止.为防止出现这种情况,你需要这样写(animals.python.price)!0,加了括号之后,在这种情况下,表达式的值会为0,如果animals或者python缺失的话.同样的逻辑对??(测试运算符)也成立: animals.python.price??和(animals.python.price)??的情况和上面说的一样.

FreeMarker中文手册--KLW 数据模型初览


数据模型初览


正如你已经见过,数据模型基本上是一棵树。这棵树可以任意地深和复杂,例如:

(root)
|
+- animals
| |
| +- mouse
| | |
| | +- size = "small"
| | |
| | +- price = 50
| |
| +- elephant
| | |
| | +- size = "large"
| | |
| | +- price = 5000
| |
| +- python
| |
| +- size = "medium"
| |
| +- price = 4999
|
+- test = "It is a test"
|
+- whatnot
|
+- because = "don't know"

变量(起类似树干目录作用的,如root,animals,mouse,elephant,python,whatnot)被称作散列(哈希),散列中存着其他变量(子变量),可以根据名字查找(如 animals,mouse,price)

只存单个值的变量(size,price,test和because)称为标量scalars。

当你想在模版中使用子变量时,需要指明从root到该节点的路径,使用小数点”.”作为分隔符。访问mouse中的price,首先从root开始,到达animals,然后进入mouse,再进入price,所以你需要这样写animals.mouse.price(root,根节点是逻辑上的,实际上不存在,不用写---KLW注)。当你在一个表达式两端加上${ . . . }时,你就告诉了FreeMarker在这里输入相应文本。

还有一种更重要的变量:序列(数组)。它们和散列类似,但是他们不存储变量名,而按照数组方式保存子变量,你可以使用数字索引访问变量。例如,在下面的数据模型中,animals和whatnot.fruits是序列(数组):

(root)
|
+- animals
| |
| +- (1st)
| | |
| | +- name = "mouse"
| | |
| | +- size = "small"
| | |
| | +- price = 50
| |
| +- (2nd)
| | |
| | +- name = "elephant"
| | |
| | +- size = "large"
| | |
| | +- price = 5000
| |
| +- (3rd)
| |
| +- name = "python"
| |
| +- size = "medium"
| |
| +- price = 4999
|
+- whatnot
|
+- fruits
|
+- (1st) = "orange"
|
+- (2nd) = "banana"

访问数组中的子变量,你可以按数组方式访问(中括号中加数字索引)。索引从0开始(编程惯例),所以第一个元素的索引是0,第二个元素的索引是1,依此类推。所以要获得第一个animal的name,你只需如此:animals[0].name。访问whatnot.fruits中的第二个元素(字符串“banana”),使用如下:whatnot.fruits[1]。

标量可以继续细化为以下几种:

  • 字符串:文本,任意序列的字符,如”m”,”o”,”u”,”s”,”e”。例如,上例中的所有name和size都是字符串。


  • 数字:数字类型的值,如上例中的所有price。字符串“50”和数字50在FreeMarker中两种完全不同的类型。前者只是一个由两个字符组成的序列(仅仅是刚好可以被我们理解为数字,而FreeMarker不会这么理解的)。后者是一个数字值,可以作代数运算。


  • 日期/时间:一个日期,或者时间,比如捕获动物的日期,或者商店开门的时间。


  • 布尔型:布尔型值,true或者false,yes或者no。比如animals可以有一个projected子变量,表示这个动物是否被保护。


摘要:

  • 数据模型可以用数结构可视化地表示

  • 标量存单个值:这个值可以是字符串、数字、日期时间或者布尔类型。

  • 散列值中包含着其他变量,同时它们拥有一个全局独一无二的名字。

  • 序列(数组)中按照数组方式存值,值可以用从0开始的索引访问。


FreeMarker中文手册--KLW 模版 + 数据模型 = 输入

模版 + 数据模型 = 输入

假设你需要一个HTML页面在电子商店应用中,如下:

<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>Welcome Big Joe!</h1>
<p>Our latest product:
<a href="products/greenmouse.html">green mouse</a>!
</body>
</html>

假设用户名(上例中的“Big Joe”)应该依赖于谁登录进这个页面,而最新商品应该从数据库中读取,因此潜在地,这些内容是经常改变的。在这种情况下,我们不能直接在HTML中输入用户名、URL和最新商品的名字,换句话说,我们不能使用静态HTML。

对这个问题的解决办法,FreeMarker使用一个模版,而不是静态HTML页面。这个模版和静态页面类似,除了它包含一些指令(让FreeMarker将页面变成动态的):

<html>
<head>
<title>Welcome!</title>
</head>
<body>
<h1>Welcome ${user}!</h1>
<p>Our latest product:
<a href="${latestProduct.url}">${latestProduct.name}</a>!
</body>
</html>

这个模版存储在web服务器上,就像静态HTML页面一样。但是,无论谁访问这个页面时,FreeMarker会即使介入并翻译这个模版,把所有的 ${...}替换成最新的内容(比如,将${user} 替换成“Big Joe”或者其他登录者的用户名),然后把结果发送给访问者的Web浏览器,所以访问者的浏览器会接收到像第一个例子中的HTML页面(纯HTML没有FreeMarker指令),不会察觉到服务器已经使用了FreeMarker。模版文件本身(存储在服务器端)不会改变。这个翻译过程对每次访问都会发生。这保证了显示的信息总是最新的。

现在,你也许已经注意到模版没有包含任何指令来找出谁是当前访问者,或者查询数据库获得最新产品。它看起来好像已经知道这些值。而实际上,这里隐藏了一个FreeMarker的重要思想(实际上是MVC的),显示逻辑和业务逻辑应该分离。在模版中只处理显示相关的事情,比如可视的图样、格式等。显示的数据(如用户名等)在FreeMarker之外准备,比如经常用java语言或者其他通用语言。所以模版作者不用知道这些值是如何计算的。实际上,这些值的计算方法可以完全改变,而模版仍可保持不变,同样,不改变其他任何东西只修改模版可使页面外观完全改变。当模版设计者和编程人员是不同人时这种分离会格外有用。

当FreeMarker(和模版作者)不关心数据是如何计算的时候,FreeMarker仍需知道实际数据是什么。所有在模版中使用的数据,都可以归入所谓的数据模型。这个数据模型由已经提到的Java等常规性语言计算。对模版作者而言,数据模型是一个树状结构(如你硬盘上的目录和文件)。在这个例子中,直观地表示如下:


(root)
|
+- user = "Big Joe"
|
+- latestProduct
|
+- url = "products/greenmouse.html"
|
+- name = "green mouse"

(为防止误解:数据模型并不是文本文件,以上只是直观的展示树状。它从java对象中来,但把这个问题留给java编程人员吧。)

把这个和你之前看到的模版相比较,${user}和${latestProduct.name}。类似地,数据模型像计算机的文件系统,root和latestProduct对应目录,而user, url 和 name对应文件。url和name在latestProduct目录下。所以latestProduct.name指的是在latestProduct目录下的name文件。这只是个类比,这里没有目录和文件。

总而言之,模版和数据是FreeMarker生成输出时需要的:
模版 + 数据模型 = 输出

FreeMarker中文手册--KLW 前言


什么是FreeMarker?


FreeMarker是一个模版引擎:一个通用的基于模版产生文本输出(可以是任何东西,从HTML到自动生成代码)。它是一个为java程序员提供的java类库,而不是一个为终端用户提供的应用程序,当然程序员也可以将FreeMarker加入到他们的产品当中。

FreeMarker设计时是用来生成HTML Web页面的,尤其是针对那些基于servlet的MVC(Model View Controller)模式的web应用程序。使用MVC模式的思想是分离设计者(HTML作者)和编程人员,使每个人都能发挥自己的长处工作。页面设计者可以修改页面外观而编程人员无需修改代码或者重新编译代码,因为程序的应用逻辑(Java程序)和页面设计(FreeMarker模版)是分离的。页面模版不会被复杂的代码打碎(早期jsp页面中常插入复杂代码---KLW注)。这种分离是很有用的,甚至对那些编程人员和页面设计者是一个人的项目,因为它能使应用程序清晰和容易维护。

尽管FreeMarker具有一定的编程能力,但是它不是象PHP那样成熟的编程语言。更确切地说,Java程序提供被显示的数据(像SQL查询),而FreeMarker仅仅使用模版生成文本页面来显示提供的数据。



FreeMarker不是一个Web应用框架。更合适地说,它是web应用程序框架的一个组件,但是FreeMarker引擎本身对HTTP或者servlet一无所知。它只负责生成文本。因此,它可以完美地应用在任何非web环境的应用程序中。注意,尽管如此,我们还是提供了可用的解决方案:把FreeMarker作为Model 2框架或者Strust的可视组件(view部分)。

FreeMarker是免费的,使用BSD格式许可证发布,它是一个OSI 认证的开源软件。OSI认证是Open Source Initiative的一个认证标记。


我应该看什么?


如果你是一个…

  • 页面设计者,你应该查阅模版作者手册,更多具体细节可以查看参考。

  • 编程人员,你应该首先查阅模版作者手册,然后再查阅编程人员手册,更多细节可以查看手册。(这些偶都还没有翻译好呢,呵呵)

FreeMarker中文手册--KLW 目录

这是一个尝试性的翻译计划,查阅FreeMarker文档的时候,看到有些人做了翻译,但是没有完全。而FreeMarker官方站的文档是很全面的。为了让更多不喜欢看英文的人看到这些文档,准备将它翻译成中文(当然是闲暇时)。如果太忙,或者没有毅力,就让它成为烂尾楼好了。偶不负责任地说,这个基本上我不会翻译完的。有些东西也是按照我自己理解的来翻译,不是直译。理解错误和实际有出入也是很正常的嘛。如果您不幸搜索看到这个页面,就将就着看吧。另外,关于版权的说明,除了这个翻译是我弄的,我不拥护这份文档的任何版权,爱干嘛干嘛。


好吧废话不说,咱们开始干吧。


目录

  • 前言


    • 什么是FreeMarker?

    • 我应该看什么?


  • 模版作者手册


    • 第一章 入门


    • 第二章 值和类型


      • 基本概念


        • 什么是值?

        • 什么是类型?

        • 数据模型是一个哈希表


      • 类型


        • 标量(基本类型)

        • 容器

        • 子程序


          • 方法和函数

          • 用户自定义指令

          • 方法/函数和用户自定义指令比较


        • 其他


          • 节点




    • 第三章 XXOO


  • 编程人员手册


2009年4月9日星期四

看 当时的月亮

看 当时的月亮 曾经代表谁的心情 结果都一样

反正最后每个人都孤独

我也不想这样 反反复复

---------------
我知道有些事情是不对的,你做过的,我也想做。比如翻看聊天记录。

2009年4月7日星期二

清明记事

清明结束,回到陌生的城市。

一。这次回到杭州的路线奇特诡异,先到临海,再转绍兴,最后到杭州。下午三点出发,晚上九点钟抵达。在临海站时,用的是站长留位票。 出行难。

二。快到杭州时因为短信问题发生了点不愉快的事情。当时我生气地想把手机撕掉,太过气愤,以致当我看到"我有点想你"时已经不怎么生气了。

三。见到堂弟,多年未见,如今已是少年,当年走时才六七岁。姑姑姑父均不同程度地发福。

四。回到村中,村中变化并不大,那些人依然是认得面孔,不晓得名字。众人说些问讯的话,我不感兴趣的内容,回答"嗯嗯嗯,我先前在北京,现在杭州。我没有碰见他。" 我知道这些事情无非是他们的谈资而已。家中房屋,风吹雨打,年久失修,主梁都已倒了。害怕自己的房子被连累,隔壁老太公得知是我们回来,激动地就差没掉眼泪,费力地说要修房子了。我无奈,说着应该的,应该的,要修房子的。

五。上坟。爷爷奶奶的坟面简陋地连石刻的名字都没有。有一支坟,仅仅知道是家中祖上,无名无姓,不知男女。

2009年4月2日星期四

和谐无处不在

gmail也被和谐了?!
刚才看到一本电子书,当然是禁书,《墓(⊙斜杠o斜杠⊙)碑--中(⊙斜杠o斜杠⊙)(⊙斜杠o斜杠⊙)(⊙斜杠o斜杠⊙)(⊙斜杠o斜杠⊙)(⊙斜杠o斜杠⊙)(⊙斜杠o斜杠⊙)(⊙斜杠o斜杠⊙)(⊙斜杠o斜杠⊙)(⊙斜杠o斜杠⊙)(⊙斜杠o斜杠⊙)实》
这里是下载地址:
当我使用gmail发送给LF和KLW的时候,惊奇地发现:
添加附件:

显示已发送:


实际上邮件还是在草稿当中: