[Flash]视频照相系列教程之三

mikel阅读(797)

到了这一步,基本上算是差不多了,在大体的框架上,就剩下预览与保存的做法了。
       第一步:预览
       在我们已经把视频,TileList组装完成后,就需要实现一个功能,就是我们需要重新预览下以前拍的照片,那么由此所引申出来的动作必然就是我们需要点 击在TileList里的小图,这一步很简单,我们先来玩一个TileList点击的小代码,后面就套一下,就很容易了。
      按照第二部分,认识TileList刚开始的那段小代码,新的代码如下:

import fl.events.ListEvent;
var i:uint;
for(i=0;i<10;i++){
  var tempMc:Object= new Object();
  tempMc.label = "PIC"+i;
  tempMc.source = new mc();
  lists.addItem(tempMc);
  
}
lists.addEventListener(ListEvent.ITEM_CLICK,thumbClick);
function thumbClick(event:ListEvent):void{
  trace(event.item.label);
}

  
      从上面简单的代码,就能知道在TileList里单击其中的图标,该怎么去实现。


      好,那么重新我们回到原来的例子上,我们已经知道TileList点击怎么实现,那么我们需要在舞台上放一个空的实例,用于加载点击图标后,传输过来的具体内容,我们这里只需要图,别的不需要。当然你可以同样传送其他信息。
      我的打算是新的预览实例放在X轴为340,Y轴为0,同时在舞台上再放一个BUTTON,实例名:down_btn,用于保存事件的触发
      预览的做法也是跟截图的做法一样,用Bitmap,和BitmapData,在前面的教程代码上继续加代码:

import fl.events.ListEvent;
var review:Sprite;
function thumbClick(event:ListEvent):void {
  var bmd:BitmapData=new BitmapData(320,240);
  bmd.draw(event.item.source);
  var bit:Bitmap=new Bitmap(bmd);
  review = new Sprite();
  addChild(review);
  review.x = 340;
  review.addChild(bit);
  //用于下载当前的图片
  //down_btn.addEventListener(MouseEvent.CLICK,downJpg);
}

       这样,这部分的功能算是完成了,也不难。下一部分的内容在于把当前的图保存到本地上,这个有很多方法,我说的是其中一种,但是大体的思路都是一样的。我先说下我的思路。
      还记得我以前写的Flash简易聊天室教程里的三板斧教程吗? 这个主要讲的是FLASH里的数据传输到外部服务器的,这里我要保存图片,那么也是把当前的图片数据传输出去,那么我们就需要CORDLIB里的一个 类:JPGEncoder类,把当前的图片数据转化成ByteArray,传输出去,如果你像我用的是PHP的话,就可以用PHP把ByteArray写 到本地上,变回原来的图片。


  
       第二步:保存
       首先我们要去http://code.google.com/p/as3corelib/把JPGEncoder类下下来。
       然后下一步就是继续完成down_btn按钮触发的下载事件构造函数:downJpg  
      

import flash.utils.ByteArray;
import com.adobe.encoding.JPEGEncoder;
function downJpg(event:MouseEvent):void {
  //?name=@@@@@@就是设置下载下来的图片名字,这个可以根据你自己想要的来修改。
  var url:String="http://localhost/downjpg.php?name=testJpg.jpg";
  var bmd:BitmapData = new BitmapData(320,240);
  bmd.draw(review);
  //下载截图质量设置
  var jpgEncoder:JPEGEncoder=new JPEGEncoder(100);
  var myByteArray:ByteArray=jpgEncoder.encode(bmd);
  
  //发送数据到PHP服务器端,然后自动弹出,我下面会提供一个PHP文件,供你测试,反正
  //根据我以前的做的很多东西来看,方法有N多。这是其中之一
  var header:URLRequestHeader = new URLRequestHeader ("Content-type", "application/octet-stream");
  var request:URLRequest=new URLRequest(url);
  request.requestHeaders.push(header);  
  request.method=URLRequestMethod.POST;
  request.data=myByteArray;
  
  var loader:URLLoader = new URLLoader();
  navigateToURL(request, "_blank");
}

PHP端的代码:

<?php
if ( isset ( $GLOBALS["HTTP_RAW_POST_DATA"] )) {
  $im = $GLOBALS["HTTP_RAW_POST_DATA"];
  header('Content-Type: image/jpeg');
  header("Content-Disposition: attachment; filename=".$_GET['name']);
  echo $im;  
}  else echo 'An error occured.';
?>

      你能测试出来吗?如果在下载弹出有错误的时候,你可以按F12发布,在网页上直接来试试。基本上,我这里是测试成功了。



      这个系列的教程算是圆满结束了,大体的框架我已经算是讲完了,当然小细节还有很多要做,这个就需要你去努力了。因为我最近时间不多,写的可能比较匆忙,有什么地方不对,或者你看不懂,你可以发E-MAIL给我,注意是E-MAIL。

[Flash]视频照相系列教程之二

mikel阅读(1098)

这部分,主要讲截图和TileList的应用。
  
       拍照,拍照,当然要拍很多张,然后放在一起,让自己去挑一些好的,然后再保存到电脑上。
       所以,我们需要想个办法,把当前的样子拍下来,这就是截图。
       第一步:截图
       那怎么截图呢?也很简单,用BitmapData就可以搞定。首先在舞台上加个Button,实例命名为:btn,作为截图触发按钮。代码如下:

var cameraVideo:Video = new Video();
addChild(cameraVideo);
cameraVideo.width =320;
cameraVideo.height =240;
var myCamera:Camera=Camera.getCamera();
if (myCamera==null) {
  //trace("你还没安装视频头,或者视频头未连接");
} else {
  cameraVideo.attachCamera(myCamera);
  btn.addEventListener(MouseEvent.CLICK,beginSnap);
}
//main做为测试截图容器,只是测试用,下一步就不需要这个了。
var main:Sprite = new Sprite();
main.y=260;
addChild(main);
function beginSnap(event:MouseEvent):void {
  //trace(cameraVideo.width);
  var bitmapdata:BitmapData=new BitmapData(320,240);
  bitmapdata.draw(cameraVideo);
  var bitmap:Bitmap=new Bitmap(bitmapdata);
  main.addChild(bitmap);
}

      如果你测试成功的话,应该就是可以看到点击按钮后,截图自然会出现在下方,当然目前只是在测试,只是让你明白每一步的做法。
    
      


      完 成截图后,我们需要停下来想一下。想象一下:我们拍了很多照片,然后把拍的无论是好的坏的,都扔到一个篮子里,然后我们再从里面挑选出好的,去打印成大照 片。思路很清晰,那么我们需要做的,就是这个篮子问题。FLASH的魅力就是在这个篮子上的表现上,我们可以把照片横着排,竖着排,甚至是:一会儿排成人字形,一会儿排成大字形。我想这就是FLASH的乐趣,当然,这里我只说其中一种,最工业化的一种,直接放到FLASH自带的TileList组件上。
      第二步:感受下TileList组件
      如何使用TileList组件呢,其实帮助里说的很清楚,而且这里也非常详细的说明了使用方法,如果你愿意认真的看下,后面的教程就很简单了。不过我这里还是放一个比较贴近的小教程,这样下一步,我就不用解释的那么费劲了。
      新建一个FLA文件,然后在舞台上,随意画一个圆形,或者方形,然后选择按F8转化为实例,命名:mc,然后选择:Export for Actionscript,在Class里也命名为:mc,这样就方便在代码里调用这个图。
      好,接着从组件里把TileList组件拉到舞台,长度设置为400,其他暂时默认,命名为:lists。
      开始在新的时间轴上加代码:
    

var i:uint;
for(i=0;i<10;i++){
  var tempMc:Object= new Object();
  tempMc.label = "PIC"+i;
  tempMc.source = new mc();
  lists.addItem(tempMc);
}

  
      运 行下看看,你所画的小图,有10张出现在这个组件上,而且有标签名字,别忘了Object我还可以加很多乱七八糟的自定义属性,比如toolTip这种, 意思就是移动到图片上自动出现提示标签,这个都是题外话,你感觉一下上面的代码,应该对titleList组件有点感觉了吧。
       那么好,我们回到视频照相程序上,我们要做的就是把每次截图都放到这个LIST上,但是我们目前截图是320*240的,好象太大了吧,不过你不要担心,放在LIST上的话,一切都会以LIST定义的高宽为主,意思就是:你想让截图以多大的大小放在tileList上,是要在tileList组件上设置的。  
       那么怎么设置tileList呢,这个就简单了,点击下这个组件,然后按SHIFT+F7,就会出现组件检查器,随便你怎么设置。
       第三步:组装
       这步就是把说过的东西给组装起来,拍照,截图,把图放到tileList上。代码如下:
  

//只要加一句,然后修改beginSnap构造函数就可以
//先建立一个数组,用来放截图数据
var contents:Array = new Array();
function beginSnap(event:MouseEvent):void {
  var bitmapdata:BitmapData=new BitmapData(320,240);
  bitmapdata.draw(cameraVideo);
  var bitmap:Bitmap=new Bitmap(bitmapdata);
  addChild(bitmap);
  contents.push(bitmap);
  var i:uint;
  for (i=0; i<contents.length; i++) {
    var tempObj:Object = new Object();
    tempObj.source=contents[i];
    tempObj.label="image"+[i];
  }
               //lists就是tileList的实例名,我把里面的columnWidth设置为120;rowHeight设置为90
  lists.addItemAt(tempObj,0);
}

  
      运行下看看,基本没什么大的问题,当然那个标签比较麻烦,这个没关系,你要也好,不要也好,做法有很多。这只是基础。

[Flash]视频照相系列教程之一

mikel阅读(901)

   这是我去年做的一个小作品,现在我把他写成一个系列教程,给大家练练手。其中包含了截图的用法,时间事件,TileList组件的应用等等,综合起来还算不错。
       第一步:让舞台能够显示连接的视频,让你的光辉形象显示出来
       这一步很简单,你首先建立一个FLA文档后,按F11,让库面版显示出来,然后在面版的下部,右键-新建VIDEO,命名随意,完成后,把这个视频从库拖到舞台上,由于初始大小为:160*120,你可以按照你的喜欢,设置为任何大小,我这里就设置为:320*240。并且命名此VIDEO的实例名为:cameraVideo
       接着在时间面版上,新建一个时间轴,并且命名为:action,按F9,开始写代码(后面的代码全都写在里面)。
      

//这是定义个新的相机视频,然后从当前的视频头取得内容。
var myCamera:Camera =Camera.getCamera();
//这个就比较简单了,把定义的相机视频绑定到放置的VIDEO上就可以了
cameraVideo.attachCamera(myCamera);
    

      测试下,看看,是否你的光辉形象出现了。注意:在测试的时候,跳出的设置,你应该选择:允许

      第二步:事情没那么简单,我们需要填坑
        
       ok,视频是出来了,但是事情还没那么简单,因为你需要考虑万一没装视频头怎么办?总需要来个提示吧。所以我们要把刚才的代码进行完善,弄个辨别语句。
    

var myCamera:Camera=Camera.getCamera();
if (myCamera==null) {
  //意思就是,未检测到视频头,然后该干嘛,当然你想干嘛就干嘛。
  trace("你还没安装视频头,或者视频头未连接");
} else {
  //这是检测成功了
  cameraVideo.attachCamera(myCamera);
    trace("视频头检测成功");
}

    上 面的测试是有一点小问题的,因为刚才已经测试成功过有视频头了,现在虽然拔掉视频头,但是他还是会显示连接正确,为啥呢,因为刚才的连接数据他还是存在了 本地上。你可以关掉FLASH,然后再重新测试下看看。这个没关系,因为,一般而言在网页上这个错误倒不会出现了。
    
    第三步:换种做法
    其实,我写这部分,还是有原因的,因为如果按照刚才的从库里拉VIDEO,后面截图会有点小问题,为了方便讲解,再来另一种方法,首先把舞台上的VIDEO删除掉,然后代码如下:

var cameraVideo:Video = new Video();
addChild(cameraVideo);
cameraVideo.width =320;
cameraVideo.height =240;
var myCamera:Camera=Camera.getCamera();
if (myCamera==null) {
  //意思就是,未检测到视频头,然后该干嘛,当然你想干嘛就干嘛。
  //trace("你还没安装视频头,或者视频头未连接");
} else {
  //这是检测成功了  
  cameraVideo.attachCamera(myCamera);
  //trace("视频头检测成功");
  }

     可以注意到,这里是用代码去直接召唤VIDEO了,这样更符合代码写作。
    很简单吧,非常简单。第一部分算完成了,下一部分就是专门讲tileList这个组件,和截图。

[C#]NVelocity First Steps

mikel阅读(1142)

NVelocity is a port of the excellent Apache Jakarta Velocity project. It is a very simple, easy to learn and extensible template engine.

Due to the lack of releases, support and bug fixes on the original port, the Castle Team decided to fork the project, bringing it to our code repository, fixing the bugs and improving it with more features.

First steps

The first thing you need to read about NVelocity is not even on this web site. You can find on the original Velocity web site.

Content

The following pages explain more about NVelocity, the bugs fixed and improvements made.

[Flex]使用Swiz Framework进行Flex开发

mikel阅读(825)

作者 Jon Rose 译者 张龙

Adobe Flex和AIR社区在不断延续着早期Java社区的趋势,很多人都在开发新的框架来简化或是改进Flex开发。本文将介绍Swiz Framework,它模仿了Java中的Spring Framework。Swiz Framework由Chris Scott开发,基于Apache License 2.0。

Adobe技术布道者Christophe Coenraets最近的一篇博文中讨论了Swiz Framework及其使用细节。他将Swiz Framework描述为:

Swiz的核心是个简单的控制反转框架。借助于IoC框架,应用组件(例如视图)无需实例化其依赖(所用的对象),甚至都不用查 找。在组件创建时框架会注入这些依赖(因此术语“依赖注入”也用来表示这种行为)并产生松耦合且可重用性更好的组件。Swiz IoC Framework所管理的组件叫做bean。

在Christophe给出的示例应用中,他介绍了如何使用类似于Spring的语法进行注入:

Swiz使用[Autowire]客户化元数据将bean注入到其他bean和view中。
在inSync中,contactService RemoteObject被注入到ContactController中,如下所示:
1
2
[Autowire(bean="contactService")]
public var contactService:RemoteObject;
… contactController又被注入到了View中,参见ContactForm.mxml:
 
1
2
[Autowire(bean="contactController")]
public var controller:IContactController;

Tony Hillerson还讨论了Swiz Framework的自动装配特性:

可能你没注意到,Autowire可是个好东西。我们不用管模型来自何处,它是否为单态的,只要知道需要一个模型,而且框架会给我们一个就行了。注意一点:属性需要为public的,否则Swiz无法为其赋值。

Tony和Christophe都觉得Swiz是个易于使用的框架,Christophe说到

Swiz是个易于使用且让人享受的框架。它具有非侵入的特性并引入了一些有趣的想法。

请查看项目指南来了解关于Swiz及其特性的更多信息。

查看英文原文:Flex Development with the Swiz Framework

[Flex]五分钟用Flex构建WebService应用

mikel阅读(744)

不管我们学习任何语言,第一个先接触到的是Hello World。然后才是其他方面的知识和技巧。学习Flex,你一定也接触过Hello World之类的起始应用,然后就是你可以开始接触MXML和AS3语法,控件,布局等知识。今天这个例子,是提供给已经开始学习Flex的开发人员,目 的是说我们可以通过Flex Builder,在五分钟之内,构造一个连接WebService服务,获取国内城市的天气预报应用。
在了解这个应用之前,请你先做好以下准备(其实也是开发其他Flex应用的准备):
下载Flex Builder 3并安装好它。你可以安装for eclispe插件的版本,也可以安装standalone的版本。安装后的使用并没有什么太大的差别。
接下来就让我们看看如何在五分钟之内完成一个Flex城市天气预报小应用,我想这应该比写HelloWorld更有成就感。
首先,我们会先使用已经准备好的一个天气预报的WebService服务,WebService服务来自WebXML,免费,而且查询信息来自国家气象局,地址如下:
http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl
你可以试着在浏览器内打开上面的这个连接,你会看到浏览器内返回的是XML结构的WSDL文档描述
从中,你可以找到这样的一部分代码:

<s:element name="getWeatherbyCityName">
  <s:complexType>
    <s:sequence>
      <s:element minOccurs="0" maxOccurs="1" name="theCityName" type="s:string"/>
    </s:sequence>
  </s:complexType>
</s:element>

其 中getWeatherbyCityName就是请求WS返回特定城市天气预报的函数名称,而theCityName就是城市名的参数(其实这个参数你只 要将合法的城市名以String格式传给getWeatherbyCityName就可以了。至于哪个城市在它WebService的服务之列,你可以自 己来挖掘WebService发现
截止做完上面我说的这一步,你大概花了30秒的时间
接下来,你可以通过 访问这里去进一步测试此WebService,而且可以在着手写代码前,先看看城市天气预报返回的XML数据构成,以及SOAP1.1和1.2的标准请求 和响应示例,包括HTTP的GET和POST示例。你试着,输入城市为“北京”,你将会得到的返回数据是基于XML格式的数据
http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?op=getWeatherbyCityName

<ArrayOfString>
<string>直辖市</string>
<string>北京</string>
<string>54511</string>
<string>54511.jpg</string>
<string>2009-2-20 15:14:50</string>
<string>4℃ / -6℃</string>
<string>2月20日 晴转多云</string>
<string>微风</string>
<string>0.gif</string>
<string>1.gif</string>
<string>今日天气实况:;1℃;风向/风力:南风3级;空气质量:中;紫外线强度:弱</string>
<string>
穿衣指数:天气冷,建议着棉衣、皮夹克加羊毛衫等冬季服装。年老体弱者宜着厚棉衣或冬大衣。
感冒指数:昼夜温差较大,较易发生感冒,请适当增减衣服。体质较弱的朋友请注意防护。
晨练指数:天气晴朗,空气清新,是您晨练的大好时机,建议不同年龄段的人们积极参加户外健身活动。
交通指数:天气晴朗,路面干燥,交通气象条件良好,车辆可以正常行驶。
中暑指数:温度不高,其他各项气象条件适宜,中暑机率极低。
公园指数:天气寒冷,不适宜放风筝。
防晒指数:紫外线强度较弱,建议涂擦SPF在12-15之间,PA+的防晒护肤品。
旅行指数:晴天,天冷加上有风,旅游指数一般,您在外出游玩的时候可要多穿衣服,以免感冒呀!
</string>
<string>7℃ / -3℃</string>
<string>2月21日 多云</string>
<string>微风</string>
<string>1.gif</string>
<string>1.gif</string>
<string>4℃ / -6℃</string>
<string>2月22日 多云</string>
<string>微风</string>
<string>1.gif</string>
<string>1.gif</string>
<string>
北 京位于华北平原西北边缘,市中心位于北纬39度,东经116度,四周被河北省围着,东南和天津市相接。全市面积一万六千多平方公里,辖12区6县,人口 1100余万。北京为暖温带半湿润大陆性季风气候,夏季炎热多雨,冬季寒冷干燥,春、秋短促,年平均气温10-12摄氏度。
北京是世界历史文化名 城和古都之一。早在七十万年前,北京周口店地区就出现了原始人群部落“北京人”。而北京建城也已有两千多年的历史,最初见于记载的名字为“蓟”。公元前 1045年北京成为蓟、燕等诸侯国的都城;公元前221年秦始皇统一中国以来,北京一直是中国北方重镇和地方中心;自公元938年以来,北京又先后成为辽 陪都、金上都、元大都、明清国都。1949年10月1日正式定为中华人民共和国首都。
北京具有丰富的旅游资源,对外开放的旅游景点达200多处, 有世界上最大的皇宫紫禁城、祭天神庙天坛、皇家花园北海、皇家园林颐和园,还有八达岭、慕田峪、司马台长城以及世界上最大的四合院恭王府等各胜古迹。全市 共有文物古迹7309项,其中国家文物保护单位42个,市级文物保护单位222个。北京的市树为国槐和侧柏,市花为月季和菊花。另外,北京出产的象牙雕 刻、玉器雕刻、景泰蓝、地毯等传统手工艺品驰誉世界。
</string>
</ArrayOfString>

现 在,你看到了这就是北京近三天的天气预报和北京这个城市的基本信息(来自国家气象局)。作为一名开发者,你会发现这个WebService返回的数据是 XML结构,但是各个元素的名称却都是string,没有其他特征,这种结构最适合通过Flex的ArrayCollection来承载,而不是e4x。
到这一步,你大概花了一分钟来了解WebService了
接 下来,你要开始使用Flex Builder开发一个非常简单的天气预报查询的小应用。我们这里所实现的就是简单的查询,获取WebService结果,显示出来。我们构造这个 Flex应用,首先,我们需要使用ArrayCollection,需要使用WebService类,需要处理响应,同时,需要有个简单的错误响应。有了 这些想法,我们开始着手写代码,首先在应用场景Application上加入三个标签,一个TextInput,用来输入要查询的城市;一个按钮,用来点 击时候调用WebService服务;一个Text文本区域,用来显示返回的天气预报结果。代码也非常简单:

<mx:TextInput id="cityname" text="北京"/>
<mx:Button label="Call WebService" click=""/>
<mx:TextArea id="myWeatherReport" height="100%" width="80%" editable="false" />

到了这里,你又花了一分钟的时间。
接下来,你要引入WebService服务,你需要加入一个WebService标签来帮你完成任务

<mx:WebService id="webService" wsdl="http://www.webxml.com.cn/WebServices/WeatherWebService.asmx?wsdl"
showBusyCursor="true" result="onLoad(event)" fault="faultHandler(event)"/>

这 里有几个属性,实例化id是webService,wsdl地址就是我们上面的那个wsdl地址,showBusyCursor设置为true,表示工作 状态,result事件就是当WebService返回结果时,调用一个方法,这个方法,我们叫做onLoade,传递的参数是result event对象,同样,fault事件的响应函数将是faultHandler。然后,我们给Button控件的click事件加入对于 WebService的调用"webService.getWeatherbyCityName(cityname.text)"。
到了这里,你又花了大概一分钟的时间。
最后,引入我们对于Onload和faultHandler的处理,也很简单:

  <mx:Script>
    <![CDATA[
      import mx.collections.ArrayCollection;
      import mx.utils.ArrayUtil;
      import mx.controls.Alert;
      import mx.rpc.events.ResultEvent;
      import mx.rpc.events.FaultEvent;
      private function onLoad(event:ResultEvent):void{
        //Alert.show(event.result.toString(),"WebService Results");
        var weatherInfo:ArrayCollection = (event.result) as ArrayCollection;
        for(var i:int=0;i<weatherInfo.length;i++){
          myWeatherReport.text+=weatherInfo[i]+"\n";
        }
      }
      private function faultHandler(event:FaultEvent):void{
        Alert.show(event.fault.toString(),'WebService Error');
      }
    ]]>
  </mx:Script>

截止到这里,你可能又花了大概一分半钟,总共已经花掉了四分半钟的时间。
最后30秒,留给你测试这个应用。

怎么样,是不是比做HelloWorld还有成就感?你还可以通过CSS和其他各种方式进一步美化应用,达到很酷的设计要求。
当然,我这五分钟只是一个“噱头”,真正要说的意思,就是Flex开发上手和学习的确非常简单,尤其是对于已经有Java或.net开发经验的朋友,用Flex来制作一个小的应用,更是高效快捷!

[C#]公交车路线查询系统后台数据库设计系列文章

mikel阅读(906)

在《查询算法》和《关联地名和站点》两篇文章中,已经实现了通过地名或站点进行路线查询的算法,但是在现实中,从起点到终点不一定全程都是乘车,例如,有以下3条路线:

R1: S1->S2->S3->S4->S5

R2: S6->S7->S2->S8

R3: S8->S9->S10

假如现在要从站点S1S7,如果用Inquiry查询路线,显然没有合适的乘车方案。但是S2S7相距仅仅一个站的距离,可以用步行代替,因此可以先S1乘坐R1S2再步行到S7

为了实现在乘车路线中插入步行路线,在数据库使用WalkRoute(StartStop, EndStop, Distance, Remark)(StartStop-起始站点,EndStop-目的站点,Distance-距离,Remark-备注)储存距离较近的两个站点。

加入表WalkRoute后,查询算法也要作相应的修改,其实WalkRouteRouteT0很相似,因此只需把WalkRoute看成是特殊的直达线路即可,修改后的InqueryT1如下: 


/*
查询站点@StartStops到站点@EndStops之间的一次换乘乘车路线,多个站点用'/'分开,如:
exec InquiryT1 '站点1/站点2','站点3/站点4'
*/
Create proc InquiryT1(@StartStops varchar(32),@EndStops varchar(32))
as
begin
    
declare @ss_tab table(name varchar(32))
    
declare @es_tab table(name varchar(32))
    
insert @ss_tab select Value from dbo.SplitString(@StartStops,'/')
    
insert @es_tab select Value from dbo.SplitString(@EndStops,'/')
    
if(exists(select * from @ss_tab sst,@es_tab est where sst.name=est.name))
    
begin
        
raiserror ('起点集和终点集中含有相同的站点',16,1)
        
return
    
end
    
declare @stops table(name varchar(32))
    
insert @stops select name from @ss_tab
    
insert @stops select name from @es_tab
    
declare @result table(
        StartStop 
varchar(32),
        Route1 
varchar(256),
        TransStop 
varchar(32),
        Route2 
varchar(256),
        EndStop 
varchar(32),
        StopCount 
int
    )
    
declare @count int
    
set @count=0
    
查询"步行-乘车"路线
    insert @result
    
select 
        sst.name 
as StartStop,
        
''+r1.StartStop+'步行到'+r1.EndStop as Route1,
        r1.EndStop 
as TransStop,
        r2.Route 
as Route2,
        est.name 
as EndStop,
        r2.StopCount 
as StopCount
    
from 
        
@ss_tab sst,
        
@es_tab est,
        (
select * from WalkRoute where EndStop not in (select name from @stops)) r1,
        RouteT0 r2
    
where
        sst.name
=r1.StartStop
        
and r1.EndStop=r2.StartStop
        
and r2.EndStop=est.name
    
order by r2.StopCount
    
set @count=@@rowcount
    
查询"乘车-步行"路线
    insert @result
    
select 
        sst.name 
as StartStop,
        r1.Route 
as Route1,
        r1.EndStop 
as TransStop,
        
''+r2.StartStop+'步行到'+r2.EndStop as Route2,
        est.name 
as EndStop,
        r1.StopCount 
as StopCount
    
from 
        
@ss_tab sst,
        
@es_tab est,
        RouteT0 r1,
        (
select * from WalkRoute where StartStop not in (select name from @stops)) r2
    
where
        sst.name
=r1.StartStop
        
and r1.EndStop=r2.StartStop
        
and r2.EndStop=est.name
    
order by r1.StopCount
    
set @count=@count+@@rowcount
    
    
if(@count=0)
    
begin
        
查询"乘车-乘车"路线
        insert @result
        
select
            sst.name 
as StartStop,
            r1.Route 
as Route1,
            r1.EndStop 
as TransStop,
            r2.Route 
as Route2,
            est.name 
as EndStop,
            r1.StopCount
+r2.StopCount as StopCount
        
from 
            
@ss_tab sst,
            
@es_tab est,
            (
select * from RouteT0 where EndStop not in (select name from @stops)) r1,
            RouteT0 r2
        
where
            sst.name
=r1.StartStop
            
and r1.EndStop=r2.StartStop
            
and r2.EndStop=est.name
            
and r1.Route<>r2.Route
        
order by r1.StopCount+r2.StopCount
    
end
    
select
        StartStop 
as 起始站点,
        Route1 
as 路线1,
        TransStop 
as 中转站点,
        Route2 
as 路线2,
        EndStop 
as 目的站点,
        StopCount 
as 总站点数
    
from
        
@result
end

[Velocity]Velocity用户手册

mikel阅读(890)




Velocity用户手册

英文来源: http://jakarta.apache.org/velocity/docs/user-guide.html

关于这个用户手册

Velocity用户手册是帮助页面设计者和内容提供者认识Velocity和其简单而功能强大的脚本语言――Velocity模板语言(VTL)。在手册上的许多例子,都是用Velocity插入动态的内容到网页上,但是所有的VLT例子都能应用到其他的页面和模板中。

感谢使用 Velocity

Velocity是什么?

Velocity是一个基于java的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象。

Velocity应用于web开发时,界面设计人员可以和java程序开发人员同步开发一个遵循MVC架构的web站点,也就是说,页面设计人员可以只关注页面的显示效果,而由java程序开发人员关注业务逻辑编码。Velocityjava代码从web页面中分离出来,这样为web站点的长期维护提供了便利,同时也为我们在JSPPHP之外又提供了一种可选的方案。

Velocity的能力远不止web站点开发这个领域,例如,它可以从模板(template)产生SQLPostScriptXML,它也可以被当作一个独立工具来产生源代码和报告,或者作为其他系统的集成组件使用。Velocity也可以为Turbine web开发架构提供模板服务(template service)。Velocity+Turbine提供一个模板服务的方式允许一个web应用以一个真正的MVC模型进行开发。

 

Velocity能为我们作什么?

Mud 商店例子

假设你是一家专门出售Mud的在线商店的页面设计人员,让我们暂且称它为在线MUD商店。你们的业务很旺,客户下了各种类型和数量的mud订单。他们都是通过输入用户名和密码后才登陆到你的网站,登陆后就允许他们查看订单并购买更多的mud。现在,一种非常流行的mud正在打折销售。另外有一些客户规律性的购买另外一种也在打折但是不是很流行的Bright Red Mud,由于购买的人并不多所以它被安置在页面的边缘。所有用户的信息都是被跟踪并存放于数据库中的,所以某天有一个问题可能会冒出来:为什么不使用velocity来使用户更好的浏览他们感兴趣的商品呢?

Velocity使得web页面的客户化工作非常容易。作为一个web site的设计人员,你希望每个用户登陆时都拥有自己的页面。

你会见了一些公司内的软件工程师,你发现他们每个人都同意客户应该拥有具有个性化的信息。那让我们把软件工程师应该作的事情发在一边,看一看你应该作些什么吧。

你可能在页面内嵌套如下的VTL声明:

<HTML>

<BODY>

Hello $customer.Name!

<table>

#foreach( $mud in $mudsOnSpecial )

   #if ( $customer.hasPurchased($mud) )

      <tr>

        <td>

          $flogger.getPromo( $mud )

        </td>

      </tr>

   #end

#end

</table>

foreach 的详细用法不久就会进行深入描述。重要的是,这短小的脚本能在你的网站上出现。当一个对Bright Red Mud 很感兴趣的顾客登陆的时候,同时Bright Red Mud在热卖中,这时顾客就能显著地看到。假如一个玩Terracotta Mud很久的顾客登陆,Terracotta Mud 的售卖信息就会出现在前面中间。Velocity的适用性是很巨大的,限制的只是你的创造性。

     VTL Reference 含有许多其他Velocity元素,这些元素能够共同帮助你,使你的网站更加好。当你越来越熟悉那些原理,你开始释放Velocity的能力。

Velocity模板语言(VTL):说明

VTL意味着提供最简单、最容易并且最整洁的方式合并页面动态内容。VTL使用references来在web site内嵌套动态内容,一个变量就是一种类型的reference。变量是某种类型的refreence,它可以指向java代码中的定义,或者从当前页面内定义的VTL statement得到值。下面是一个VTL statement的例子,它可以被嵌套到HTML代码中:

#set( $a = "Velocity" )

和所有的VTL statement一样,这个statement以#字符开始并且包含一个directiveset。当一个在线用户请求你的页面时,Velocity 模板引擎将查询整个页面以便发现所有#字符,然后确定哪些是VTL statement,哪些不需要VTL作任何事情。

#字符后紧跟一个directiveset时,这个set directive使用一个表达式(使用括号封闭)――一个方程式分配一个值给变量。变量被列在左边,而它的值被列在右边,最后他们之间使用=号分割。

在上面的例子中,变量是$a,而它的值是Velocity。和其他的references一样以$字符开始,而值总是以双引号封闭。Velocity中仅有String可以被赋值给变量。

使用$字符开始的references用于得到什么;使用#字符开始的directives用于作些什么。在上面的例子中,#set是分配一个值给变量。变量$a 在模板中输出 "Velocity"

 

Hello Velocity World!

一旦某个变量被分配了一个值,那么你就可以在HTML文件的任何地方引用它。在下面的例子中,一个值被分配给$foo变量,并在其后被引用。

<html>

<body>

#set( $foo = "Velocity" )

Hello $foo World!

</body>

<html>

上面的实现结果是在页面上打印“Hello Velocity World

为了使包含VTL directivesstatement更具有可读性,我们鼓励你在新行开始每个VTL statement,尽管你不是必须这么作。Set的用法将在后面详细描述。

 

注释

         注释是那些描述文本不出现在模板引擎输出里面。注释一个主要用处是提醒自己和解释出现在VTL中的声明,或是其他用途。下面是一个在VLT中的注释例子。

## This is a single line comment.

单行注释以##开始,结束在这行的结尾。如果你要写几行注释,这没有必要写几个单行注释。

多行注释,以#*开始*#结束,可以解决这个问题。

This is text that is outside the multi-line comment.

Online visitors can see it.

 

#*

 Thus begins a multi-line comment. Online visitors won't

 see this text because the Velocity Templating Engine will

 ignore it.

*#

 

Here is text outside the multi-line comment; it is visible.

这里有几个例子关于单行和多行注释如何工作。

This text is visible. ## This text is not.

This text is visible.

This text is visible. #* This text, as part of a multi-line comment,

is not visible. This text is not visible; it is also part of the

multi-line comment. This text still not visible. *# This text is outside

the comment, so it is visible.

## This text is not visible.

这里有第三种类型的注释,VLT注释块,可能用于存放文档的作者名称和版本信息。

#**

This is a VTL comment block and

may be used to store such information

as the document author and versioning

information:

@author

@version 5

*#

 

References

 

VTL中有三种类型的references:变量(variables)、属性(properties)、方法(methods)。作为一个使用VTL的页面设计者,你和你的工程师必须就references的名称达成共识,以便你可以在你的template中使用它们。

所有的  reference被作为一个String对象处理。如果有一个对象$foo是一个Integer对象,那么Velocity将调用它的toString()方法将这个对象转型为String类型。

 

变量

非正式变量是由“$”开头,接着是VTL标识符。VLT标识符必须以字母(a..z,A..Z)开头。剩下的部分限于以下几种:

l          字母(a..z,A..Z)

l          数字(0..9)

l          连字符(“-”)

l          下划线(“_”)

这里是几个在VTL中有效的变量reference

$foo

$mudSlinger

$mud-slinger

$mud_slinger

$mudSlinger1

VLT定义一个变量,例如$foo,变量能或者通过模板中的set 方法,或者通过Java 代码获得值。

例如,Java 变量 $foo 的值是 bar ,在这个模板被请求时,在网页上 bar 会替代所有 $foo 。选择地,假如包括了下面的声明:

#set( $foo = "bar" )

按照这样的设置,输出就会跟之前的一样。

 

属性

第二种有趣的VTL reference就是属性,而且属性有一种与众不同的格式。非正式变量是由“$”开头,接着是VTL标识符,再接着就是字符(“.”)和其他的VLT标识符。这里是一些在VLT中有效属性的定义:

$customer.Address

$purchase.Total

在第一个例子中$customer.Address有两种含义。它可以表示:查找hashtable对象customer中以Address为关键字的值;也可以表示调用customer对象的getAddress()方法。当你的页面被请求时,Velocity将确定以上两种方式选用那种,然后返回适当的值。

 

方法

一个方法就是被定义在java中的一段代码,并且它有完成某些有用工作的能力,例如一个执行计算和判断条件是否成立、满足等。方法是一个由$开始并跟随VTL标识符组成的References,一般还包括一个VTL方法体。一个VTL方法体包括一个VLT标识接着一个左括号(“(”),接着是参数列表,再接着是右括号(“)”)。这里是一些在VTL中有效的方法定义:

$customer.getAddress()

$purchase.getTotal()

$page.setTitle( "My Home Page" )

$person.setAttributes( ["Strange", "Weird", "Excited"] )

前两个例子$customer.getAddress()$purchase.getTotal()看起来挺想上面的属性$customer.Address $purchase.Total。如果你觉得他们之间有某种联系的话,那你是正确的。

VTL属性可以作为VTL方法的缩写。$customer.Address属性和使用$customer.getAddress()方法具有相同的效果。如果可能的话使用属性的方式是比较合理的。属性和方法的不同点在于你能够给一个方法指定一个参数列表。

非正式定义能够用下面的方法:

$sun.getPlanets()

$annelid.getDirt()

$album.getPhoto()

我们期待那些方法返回属于太阳系的行星的名称,喂养我们的蚯蚓,或者从相册里面取出一张照片。只有长符号为下面的方法服务。

$sun.getPlanet( ["Earth", "Mars", "Neptune"] )

## Can't pass a parameter list with $sun.Planets

 

$sisyphus.pushRock()

## Velocity assumes I mean $sisyphus.getRock()

 

$book.setTitle( "Homage to Catalonia" )

## Can't pass a parameter list

 

正式reference标记

非正式references用于上述的例子中。但是同样有正式的references,如下面所示:

${mudSlinger}

${customer.Address}

${purchase.getTotal()}

在几乎所有场合你都可以使用非正式references,但是在某些场合,只能使用正式reference 才能正确处理。

设想你创建一个句子:$vice 作为句子的名词 。目标是为了使某些人选择不同的词,产生下面两种结果之一:"Jack is a pyromaniac." 或者 "Jack is a kleptomaniac."。使用非正式定义不太适合用于这种情况。看一下下面的例子:

Jack is a $vicemaniac.

本来变量是$vice现在却变成了$vicemaniac,这样Veloctiy就不知道您到底要什么了。所以,应该使用正是格式书写

Jack is a ${vice}maniac.

现在Velocity就知道reference $vice,而不是$vicemaniac。正式定义经常用于模板中refernces与文本连接在一起的情况。

 

Quiet reference notation

Velocity遇到没有定义的reference,通常它会直接输出reference。例如:假如下面的reference出现在一个VTL模板中:

<input type="text" name="email" value="$email"/>

form 最初加载的时候,变量$email 没有值,但你想出现一个空白的文本框设定值为“$email”。

使用quiet reference notation可以使Velocity正常显示,你需要用$!email ,代替$email。所以上面的例子,会改成下面:

<input type="text" name="email" value="$!email"/>

这样文本框的初始值就不会是email而是空值了。

正式和quiet格式的reference notation也可一同使用,像下面这样:

<input type="text" name="email" value="$!{email}"/>

 

Getting literal

Velocity使用特殊字符$#来帮助它工作,所以如果要在template里使用这些特殊字符要格外小心。本节将讨论$字符。

货币字符

这是没有问题的:"I bought a 4 lb. sack of potatoes at the farmer's market for only $2.50!"VTL中使用$2.5这样的货币标识是没有问题得的,VTL不会将它错认为是一个reference,因为VTL中的reference总是以一个大写或者小写的字母开始。

 

Escaping valid VTL reference

某些情况使用Velocity可能会觉得很烦恼。逃避特殊符是处理出现在你的模板中VTL特殊符有效方法,就是使用反斜杠(“\”)

#set( $email = "foo" )

$email

假如Velocity在你的模板中遇到 $email ,它会搜索上下文,得到相应的值。这里的输出是 foo ,因为 $email 被定义了。假如  $email 没有被定义,输出会是 $email

设想 $email 被定义了(例如,它的值是 foo ),而且你想输出 $email 。这里有几种方法能达到目的,但是最简单的是使用逃避符。

## The following line defines $email in this template:

#set( $email = "foo" )

$email

\$email

\\$email

\\\$email

将显示为:

foo

$email

\foo

\$email

注意到 \ 屏蔽了左边的 $”。屏蔽左边规则,使得 \\\$email 显示为 \\$email 。那些例子与 $email没有定义相比较。

$email

\$email

\\$email

\\\$email

将显示为:

$email

\$email

\\$email

\\\$email

注意Velocity处理 定义了的references 与没有定义的不一样。这里 set $foo 的值为 gibbous

#set( $foo = "gibbous" )

$moon = $foo

输出会是:$moon = gibbous ,$moon 按照字面上输出因为它没有定义,gibbous 替代 $foo 输出。

避开VTL directives 还有其他方法,在 Directives 那章节会更详细描述。

 

Case substitution

现在你已经对reference比较熟悉了,你可以将他们高效的应用于你的template了。Velocity利用了很多java规范以方便了设计人员的使用。例如:

$foo

 

$foo.getBar()

## is the same as

$foo.Bar

 

$data.getUser("jon")

## is the same as

$data.User("jon")

 

$data.getRequest().getServerName()

## is the same as

$data.Request.ServerName

## is the same as

${data.Request.ServerName}

那些例子说明了同样的references 用法。Velocity 利用Java instrospection bean features解决 reference 的名称对于对象和对象方法的问题。它可以出入你的模板中和求出 references 的值。

Velocity 是模仿Sun微系统中的Bean规范定义的,因而它是很灵活的。然而,它的开发者已经很努力地捕捉和纠正可能出现的错误。当方法 getFoo() 在模板中 $bar.foo调用时,Velocity首先尝试 $getfoo。如果失败,它会继续尝试 $getFoo。同样地,当模板查询 $bar.FooVelocity会先尝试 $getFoo(),然后再尝试 $getfoo()

但是,注意VTL中不会将reference解释为对象的实例变量。例如:$foo.Name将被解释为Foo对象的getName()方法,而不是Foo对象的Name实例变量。

 

Directives

Reference允许设计者使用动态的内容,而directive使得你可以应用java代码来控制你的显示逻辑,从而达到你所期望的显示效果。

#set

#set标志是用于对一个reference赋值。值会赋给一个变量或者一个属性,而且赋值会在括号里出现:

#set( $primate = "monkey" )

#set( $customer.Behavior = $primate )

左边(LHS) 一定是一个变量或者一个属性。右边(RHS)可以是下面中的一个类型:

l          变量

l          字符串

l          属性

l          方法

l          数字

l          数组

这些例子显示上述的每一种类型:

#set( $monkey = $bill ) ## variable reference

#set( $monkey.Friend = "monica" ) ## string literal

#set( $monkey.Blame = $whitehouse.Leak ) ## property reference

#set( $monkey.Plan = $spindoctor.weave($web) ) ## method reference

#set( $monkey.Number = 123 ) ##number literal

#set( $monkey.Say = ["Not", $my, "fault"] ) ## ArrayList

注意:最后一个例子的取值方法为:$monkey.Say.get(0)

RHS也可以是一个简单的算术表达式:

#set( $value = $foo + 1 )

#set( $value = $bar – 1 )

#set( $value = $foo * $bar )

#set( $value = $foo / $bar )

如果你的RHS是一个nullVTL的处理将比较特殊:它将指向一个已经存在的reference,这对初学者来讲可能是比较费解的。例如:

#set( $result = $query.criteria("name") )

The result of the first query is $result

 

#set( $result = $query.criteria("address") )

The result of the second query is $result

如果$query.criteria(“name”)返回一个“bill”,而$query.criteria(“address”)返回的是null,则显示的结果如下:

The result of the first query is bill

 

The result of the second query is bill

这容易使新手糊涂:创建一个#foreach循环,企图想通过一个属性或者一个方法 #set一个reference,然后马上就用 #if 测试。例如:

#set( $criteria = ["name", "address"] )

 

#foreach( $criterion in $criteria )

 

    #set( $result = $query.criteria($criterion) )

 

    #if( $result )

        Query was successful

    #end

 

#end

在上面的例子中,程序将不能智能的根据$result的值决定查询是否成功。在$result#set后(added to the context),它不能被设置回nullremoved from the context)。打印的结果将显示两次查询结果都成功了,但是实际上有一个查询是失败的。

为了解决以上问题我们可以通过预先定义的方式:

#set( $criteria = ["name", "address"] )

 

#foreach( $criterion in $criteria )

 

    #set( $result = false )

    #set( $result = $query.criteria($criterion) )

 

    #if( $result )

        Query was successful

    #end

 

#end

不像其他Velocity指示符号,#set 没有一个#end 结束。

 

String Literals

当你使用#set directiveString literal封闭在一对双引号内。象下面:

#set( $directoryRoot = "www" )

#set( $templateName = "index.vm" )

#set( $template = "$directoryRoot/$templateName" )

$template

上面这段代码的输出结果为:

www/index.vm

但是,当string literal被封装在单引号内时,它将不被解析:

#set( $foo = "bar" )

$foo

#set( $blargh = '$foo' )

$blargh

输出为:

bar $foo

上面这个特性可以通过修改velocity.properties文件的stringliterals.interpolate = false的值来改变上面的特性是否有效。

 

条件语句

If / ElseIf / Else

当一个web页面被生成时使用Velocity#if directrive,如果条件成立的话可以在页面内嵌入文字。例如:

#if( $foo )

   <strong>Velocity!</strong>

#end

上例中的条件语句将在以下两种条件下成立:(i)$foo是一个boolean型的变量,且它的值为true(ii)$foo变量的值不为null。这里需要注意一点:Velocity context仅仅能够包含对象,所以当我们说“boolean”时实际上代表的时一个Boolean对象。即便某个方法返回的是一个boolean值,Velocity也会利用内省机制将它转换为一个Boolean的相同值。

如果条件成立,那么#if#end之间的内容将被显示。在这个例子中,如果 $foo的值为true,输出为“Velocity!”。相反地,如果 $foo 是一个 null值,或者是一个 false 值,表达式值为 false,没有输出。

#elseif#else元素可以同#if一同使用。注意:Velocity 模板引擎遇到一个为true值的表达式就会停止。在下面的例子,假设 $foo=15,$bar=6

#if( $foo < 10 )

    <strong>Go North</strong>

#elseif( $foo == 10 )

    <strong>Go East</strong>

#elseif( $bar == 6 )

    <strong>Go South</strong>

#else

    <strong>Go West</strong>

#end

在这个例子中,$foo10大,所以在开始的两个比较中都失败。接着 $bar6比较是真的所以输出为 GO South

注意这里的Velocity的数字是作为Integer来比较的――其他类型的对象将使得条件为false,但是与java不同它使用==来比较两个值,而且velocity要求等号两边的值类型相同。

 

关系、逻辑运算符

Velocity中使用等号操作符判断两个变量的关系。这里有个简单例子关于等于号的使用:

#set ($foo = "deoxyribonucleic acid")

#set ($bar = "ribonucleic acid")

 

#if ($foo == $bar)

  In this case it's clear they aren't equivalent. So…

#else

  They are not equivalent and this will be the output.

#end

VelocityANDORNOT逻辑运算符。想得到更多信息,请看VTL Reference Guide。下面的例子是说明ANDORNOT逻辑运算符的用法:

## logical AND

 

#if( $foo && $bar )

   <strong> This AND that</strong>

#end

只有当$foo $bar 都为true#if 才会得到true值。如果$foofalse ,表达式的值为 false$bar 就不会求值。如果 $foo的值为 true Velocity模板引擎会检查 $bar的值,如果$bar=true,整个表达式的值为true,输出为“This AND that”。如果$bar=false,整个表达式的值为false,没有输出。

逻辑OR 的工作方式一样,除了只要有一个值为true ,整个表达式的值就为true。考虑一下下面的例子。

## logical or

 

#if( $foo || $bar )

    <strong>This or That</strong>

#end

如果$footrueVelocity模板引擎就没有必要查找 $bar,无论 $bar true 还是 false ,表达式的值为 true ,输出为“This or That”。如果$fooflase$bar的值就一定要检查,在这个例子中,如果 $bar 同样是false,表达式的值为 false ,没有输出。从另外一个角度看,如果$bar 的值为true ,整个表达式的值为 true ,输出为 This or That”。

关于逻辑NOT ,只有一个疑问:

##logical NOT

 

#if( !$foo )

  <strong>NOT that</strong>

#end

如果 $foo=true, !$foo 的值为 false,没有输出。如果$foo=false!$foo的值为true,输出为“NOT that”。注意不要跟 quiet reference $!foo 混为一谈,那是完全不一样的。

 

循环

Foreach循环

#foreach 用于循环。例子:

<ul>

#foreach( $product in $allProducts )

    <li>$product</li>

#end

</ul>

每次循环$allProducts中的一个值都会赋给$product变量。

$allProducts可以是一个VectorHashtable或者Array。分配给$product的值是一个java对象,并且可以通过变量被引用。例如:如果$product是一个javaProduct类,并且这个产品的名字可以通过调用他的getName()方法得到。

现在我们假设$allProducts是一个Hashtable,如果你希望得到它的key应该像下面这样:

<ul>

#foreach( $key in $allProducts.keySet() )

    <li>Key: $key -> Value: $allProducts.get($key)</li>

#end

</ul>

Velocity还特别提供了得到循环次数的方法,以便你可以像下面这样作:

<table>

#foreach( $customer in $customerList )

    <tr><td>$velocityCount</td><td>$customer.Name</td></tr>

#end

</table>

$velocityCount变量的名字是Velocity默认的名字,你也可以通过修改velocity.properties文件来改变它。默认情况下,计数从1”开始,但是你可以在velocity.properties设置它是从1”还是从0”开始。下面就是文件中的配置:

# Default name of the loop counter

# variable reference.

directive.foreach.counter.name = velocityCount

 

# Default starting value of the loop

# counter variable reference.

directive.foreach.counter.initial.value = 1

 

include

#include script element允许模板设计者引入本地文件。被引入文件的内容将不会通过模板引擎被render。为了安全的原因,被引入的本地文件只能在TEMPLATE_ROOT目录下。

#include( "one.txt" )

#include 引用的文件用引号括起来。

如果您需要引入多个文件,可以用逗号分隔就行:

#include( "one.gif","two.txt","three.htm" )

在括号内可以是文件名,但是更多的时候是使用变量的。这用于根据页面提交的需求而输出。这里有一个例子同时有文件名和变量。

#include( "greetings.txt", $seasonalstock )

 

parse

#parse script element允许模板设计者一个包含VTL的本地文件。Velocity将解析其中的VTLrender模板。

#parse( "me.vm" )

就像#include#parse接受一个变量而不是一个模板。任何由#parse指向的模板都必须包含在TEMPLATE_ROOT目录下。与#include不同的是,#parse只能指定单个对象。

你可以通过修改velocity.properties文件的parse_direcive.maxdepth的值来控制一个template可以包含的最多#parse的个数――默认值是10#parse是可以递归调用的,例如:如果dofoo.vm包含如下行:

Count down.

#set( $count = 8 )

#parse( "parsefoo.vm" )

All done with dofoo.vm!

那么在parsefoo.vm模板中,你可以包含如下VTL

$count

#set( $count = $count – 1 )

#if( $count > 0 )

    #parse( "parsefoo.vm" )

#else

    All done with parsefoo.vm!

#end

在显示“Count down”后,Velocity通过parsefoo.vm,从8往下数。当计数到了0,它就会显示“All done with parsefoo.vm!”。在这时,Velocity 会返回到dofoo.vm ,输出信息:“All done with dofoo.vm!”。

 

 

停止

#stop script element允许模板设计者停止执行模板引擎并返回。把它应用于Debug是很有帮助的。

#stop

 

 

Velocimacros

#macro script element允许模板设计者定义一段可重用的VTL templateVelocimacros广泛用于简单和复杂的行列。Velocimacros的出现是为了减少编码和极小化排版错误,对Velocimacros的概念提供一个介绍。

#macro( d )

<tr><td></td></tr>

#end

在上面的例子中Velocimacro被定义为d,然后你就可以在任何VTL directive中以如下方式调用它:

#d()

当你的template被调用时,Velocity将用<tr><td></td></tr>替换为#d()

每个Velocimacro可以拥有任意数量的参数――甚至0个参数,虽然定义时可以随意设置参数数量,但是调用这个Velocimacro时必须指定正确的参数。下面是一个拥有两个参数的Velocimacro,一个参数是color另一个参数是array

#macro( tablerows $color $somelist )

#foreach( $something in $somelist )

    <tr><td bgcolor=$color>$something</td></tr>

#end

#end

Velocimacro在这个例子的定义,tablerows,有两个元素。第一个是替换 $color ,第二个是替换$somelist

任何东西都可以通过Velocimacro加入到VTL模板中。tablerows Velocimacro 是一个foreach 标识。 这里有两个#end标识在#tablerows Velocimacro 定义里面。第一个是属于 #foreach,第二个是属于Velocimacro定义的。

#set( $greatlakes = ["Superior","Michigan","Huron","Erie","Ontario"] )

#set( $color = "blue" )

<table>

    #tablerows( $color $greatlakes )

</table>

注意到$greatlakes 替代 $somelist。当#tablerows Velocimacro被调用时,就会产生下面的输出:

<table>

    <tr><td bgcolor="blue">Superior</td></tr>

    <tr><td bgcolor="blue">Michigan</td></tr>

    <tr><td bgcolor="blue">Huron</td></tr>

    <tr><td bgcolor="blue">Erie</td></tr>

    <tr><td bgcolor="blue">Ontario</td></tr>

</table>

Velocimacros可以在Velocity模板内实现行内定义(inline),也就意味着同一个web site内的其他Velocity模板不可以获得Velocimacros的定义。定义一个可以被所有模板共享的Velocimacro显然是有很多好处的:它减少了在一大堆模板中重复定义的数量、节省了工作时间、减少了出错的几率、保证了单点修改。

上面定义的#tablerows( $color $list )Velocimacro被定义在一个Velocimacros模板库(velocity.properties中定义)里,所以这个macro可以在任何规范的模板中被调用。它可以被多次应用并且可以应用于不同的目的。例如下面的调用:

#set( $parts = ["volva","stipe","annulus","gills","pileus"] )

#set( $cellbgcol = "#CC00FF" )

<table>

#tablerows( $cellbgcol $parts )

</table>

当为mushroom.vm实现一个请求时,Velocity会在模板库中找到#tablerows Velocimacro(定义在velocity.properties文件),产生以下输出:

<table>

    <tr><td bgcolor="#CC00FF">volva</td></tr>

    <tr><td bgcolor="#CC00FF">stipe</td></tr>

    <tr><td bgcolor="#CC00FF">annulus</td></tr>

    <tr><td bgcolor="#CC00FF">gills</td></tr>

    <tr><td bgcolor="#CC00FF">pileus</td></tr>

</table>

 

Velocimacro arguments

Velocimacro可以使用以下任何元素作为参数:

l          Reference:任何以$开头的reference

l          String literal

l          Number literal

l          IntegerRange[1….3]或者[$foo….$bar]

l          对象数组:[“a”,”b”,”c”]

l          boolean值:truefalse

当将一个reference作为参数传递给Velocimacro时,请注意reference作为参数时是以名字的形式传递的。这就意味着参数的值在每次Velocimacro内执行时才会被产生。这个特性使得你可以将一个方法调用作为参数传递给Velocimacro,而每次Velocimacro执行时都是通过这个方法调用产生不同的值来执行的。例如:

     #macro( callme $a )

         $a $a $a

     #end

     #callme( $foo.bar() )

执行的结果是:reference $foobar()方法被执行了三次。

         在第一次扫描中,出现惊人的作用。但是当你考虑到Velocimacro的最原始动机:消除剪切、粘贴、复制,简单地使用VTL使它更灵活。它允许你对传递对象到Velocimacro,例如一个产生重复序列颜色,着色表格的行的对象。

如果你不需要这样的特性可以通过以下方法:

#set( $myval = $foo.bar() )

#callme( $myval )

 

Velocimacro properties

Velocity.properties文件中的某几行能够使Velocimacros的实现更加灵活。注意更多的内容可以看Developer Guide

Velocity.properties文件中的velocimacro.libraary:一个以逗号分隔的模板库列表。默认情况下,velocity查找唯一的一个库:VM_global_library.vm。你可以通过配置这个属性来指定自己的模板库。

         Velocity.properties文件中的velocimacro.permissions.allow.inline属性:有两个可选的值true或者false,通过它可以确定Velocimacros是否可以被定义在regular template内。默认值是ture――允许设计者在他们自己的模板中定义Velocimacros

         Velocity.properties文件中的velocimacro.permissions.allow.inline.replace.global属性有两个可选值truefalse,这个属性允许使用者确定inlineVelocimacro定义是否可以替代全局Velocimacro定义(比如在velocimacro.library属性中指定的文件内定义的Velocimacro)。默认情况下,此值为false。这样就阻止本地Velocimacro定义覆盖全局定义。

         Velocity.properties文件中的velocimacro.permissions.allow.inline.local.scale属性也是有truefalse两个可选值,默认是false。它的作用是用于确定你inline定义的Velocimacros是否仅仅在被定义的template内可见。换句话说,如果这个属性设置为true,一个inline定义的Velocimacros只能在定义它的template内使用。你可以使用此设置实现一个奇妙的VM窍门:a template can define a private implementation of the second VM that will be called by the first VM when invoked by that template. All other templates are unaffected

Velocity.properties文件中的velocimacro.context.localscope属性有truefalse两个可选值,默认值为false。当设置为true时,任何在Velocimacro内通过#set()context的修改被认为是针对此velocimacro的本地设置,而不会永久的影响内容。

         Velocity.properties文件中的velocimacro.library.autoreload属性控制Velocimacro库的自动加载。默认是false。当设置为ture时,对于一个Velocimacro的调用将自动检查原始库是否发生了变化,如果变化将重新加载它。这个属性使得你可以不用重新启动servlet容器而达到重新加载的效果,就像你使用regular模板一样。这个属性可以使用的前提就是resource loader缓存是off状态(file.resource.loader.cache = false)。注意这个属性实际上是针对开发而非产品的。

 

Velocimacro Trivia

Velocimacro必须被定义在他们被使用之前。也就是说,你的#macro()声明应该出现在使用Velocimacros之前。

特别要注意的是,如果你试图#parse()一个包含#macro()的模板。因为#parse()发生在运行期,但是解析器在parsetiem决定一个看似VM元素的元素是否是一个VM元素,这样#parse()-ing一组VM声明将不按照预期的样子工作。为了得到预期的结果,只需要你简单的使用velocimacro.library使得Velocity在启动时加载你的VMs

 

Escaping VTL directives

VTL directives can be escaped with “\”号,使用方式跟VTLreference使用逃逸符的格式差不多。

## #include( "a.txt" ) renders as <contents of a.txt>

#include( "a.txt" )

 

## \#include( "a.txt" ) renders as \#include( "a.txt" )

\#include( "a.txt" )

 

## \\#include ( "a.txt" ) renders as \<contents of a.txt>

\\#include ( "a.txt" )

在对在一个directive内包含多个script元素的VTL directives使用逃逸符时要特别小心(比如在一个if-else-end statement内)。下面是VTLif-statement的典型应用:

#if( $jazz )

    Vyacheslav Ganelin

#end

如果$jazzture,输出将是:

Vyacheslav Ganelin

如果$jazzfalse,将没有输出。使用逃逸符将改变输出。考虑一下下面的情况:

\#if( $jazz )

    Vyacheslav Ganelin

\#end

现在无论$jazztrue还是false,输出结果都是:

#if($ jazz )

     Vyacheslav Ganelin

 #end

事实上,由于你使用了逃逸符,$jazz根本就没有被解析为boolean型值。在逃逸符前使用逃逸符是合法的,例如:

\\#if( $jazz )

   Vyacheslav Ganelin

\\#end

以上程序的显示结果为:

\ Vyacheslav Ganelin

\

         为了方便理解,注意到 #if(arg)当新一行的结束(return)在输出中会省略新行。因此,#if()块是接着第一个‘\’,显示来自在#if()前面的‘\\’。最后的‘\’在跟文本不同的行,因为那里有新的一行在‘Ganelin’后面,所以最后‘\\’在#end 的前面。

         但是如果$jazzfalse,那么将没有输出。注意:事情也有例外,如果script elements 没有完全的逃避。

\\\#if( $jazz )

    Vyacheslave Ganelin

\\#end

         在这里#if 已经逃避了,但是#end没有,有太多的#end 的产生解析错误。

VTLFormatting issues

尽管在此用户手册中VTL通常都开始一个新行,如下所示:

#set( $imperial = ["Munetaka","Koreyasu","Hisakira","Morikune"] )

#foreach( $shogun in $imperial )

    $shogun

#end

         但是像下面这种写法也是可以的:

Send me #set($foo = ["$10 and ","a cake"])#foreach($a in $foo)$a #end please.

         上面的代码可以被改写为:

Send me

#set( $foo = ["$10 and ","a cake"] )

#foreach( $a in $foo )

$a

#end

please.

或者

Send me

#set($foo       = ["$10 and ","a cake"])

                 #foreach           ($a in $foo )$a

         #end please.

每一种的输出结果将一样。

 

 

其他特性和杂项

math

在模板中可以使用Velocity内建的算术函数,如:加、减、乘、除。下面的等式分别是:加、减、乘、除:

#set( $foo = $bar + 3 )

#set( $foo = $bar – 4 )

#set( $foo = $bar * 6 )

#set( $foo = $bar / 2 )

当执行除法时将返回一个Integer类型的结果。而余数你可以使用%来得到:

#set( $foo = $bar % 5 )

Velocity内使用数学计算公式时,只能使用像-n,-2,-1,0,1,2,n这样的整数,而不能使用其它类型数据。当一个非整型的对象被使用时它将被logged并且将以null作为输出结果。

 

Range Operator

Range operator可以被用于与#set#foreach statement联合使用。对于处理一个整型数组它是很有用的,Range operator具有以下构造形式:

[n..m]

mn都必须是整型,而m是否大于n则无关紧要。例子:

First example:

#foreach( $foo in [1..5] )

$foo

#end

 

Second example:

#foreach( $bar in [2..-2] )

$bar

#end

 

Third example:

#set( $arr = [0..1] )

#foreach( $i in $arr )

$i

#end

 

Fourth example:

[1..3]

产生以下输出:

First example:

1 2 3 4 5

 

Second example:

2 1 0 -1 -2

 

Third example:

0 1

 

Fourth example:

[1..3]

注意:range operator只在#set#foreach中有效。

         网页设计者使用标准尺寸制作表格,但是某时候会没有足够的数据填入表格,就会发现 range 非常有用。

 

Advanced IssueEscaping and

当一个reference分隔时,并且在它之前有逃逸符时,reference将以特殊的方式处理。注意这种方式与标准的逃逸方式时不同的。对照如下:

#set( $foo = "bar" )

$\!foo

$\!{foo}

$\\!foo

$\\\!foo

         这将显示为:

$!foo

$!{foo}

$\!foo

$\\!foo

         \ $ 前面跟逃避规则相比较;

\$foo

\$!foo

\$!{foo}

\\$!{foo}

         这将显示为:

\$foo

\$!foo

\$!{foo}

\bar

 

Velocimacro杂记

         这章节是一个小型涉及Velocimacros常见问题解答。这章节会随时间的变化而变化,所以它的作用有时是检验新的信息。

         注意:在这章节'Velocimacro'简称为‘VM’。

 

1.        我能用一个标识或者另外的VM 作为一个VM的参数吗?

例如:#center ( #bold( “hello” ) )

 

答:不可以。一个directive的参数使用另外一个directive是不合法的。

但是,还是有些事情你可以作的。最简单的方式就是使用双引号,所以你可以这样做:

#set($stuff = "#bold('hello')" )

#center( $stuff )

         上面的格式也可以缩写为一行:

#center( "#bold( 'hello' )" )

         请注意在下面的例子中参数被evaluatedVelocimacro内部,而不是在calling level。例子:

 

#macro( inner $foo )

  inner : $foo

#end

 

#macro( outer $foo )

   #set($bar = "outerlala")

   outer : $foo

#end

 

#set($bar = 'calltimelala')

#outer( "#inner($bar)" )

 

输出结果为:

Outer : inner : outerlala

         因为#inner($bar)的赋值发生在#outer(),所以$bar的值在#outer()赋给的。

记住Veloctiy的特性:参数的传递是By Name的。例如:

#macro( foo $color )

  <tr bgcolor=$color><td>Hi</td></tr>

  <tr bgcolor=$color><td>There</td></tr>

#end

 

#foo( $bar.rowColor() )

         以上代码将导致rowColor()方法两次调用,而不是一次。为了避免这种现象的出现,我们可以按照下面的方式执行:

#set($color = $bar.rowColor())

#foo( $color )

 

2.我能通过#parse()登记velocimacros吗?

 

答:目前,Velocimacros必须在第一次被模板调用前被定义。这就意味着你的#macro()声明应该出现在使用Velocimacros之前。

如果你试图#parse()一个包含#macro() directive的模板,这一点是需要牢记的。因为#parse()发生在运行期,但是解析器在parsetiem决定一个看似VM元素的元素是否是一个VM元素,这样#parse()-ing一组VM声明将不按照预期的样子工作。为了得到预期的结果,只需要你简单的使用velocimacro.library使得Velocity在启动时加载你的VMs

 

3.什么是velocimacro自动加载?

 

答:velocimacro.library.autoreload是专门为开发而非产品使用的一个属性。此属性的默认值是false

<type>.resource.loader.cache = false 连同一起设为true ()(<type>是你用的资源器,例如‘file),当你编译它们的时候Velocity引擎会自动重载那些在你的velocimacro库文件中的修改,所以你不用servlet引擎(或者应用程序)或者其他窍门来使你的velocimacros重载。

         这里有一个简单的属性配置。

   file.resource.loader.path = templates

   file.resource.loader.cache = false

   velocimacro.library.autoreload = true

    不要在你的作品中出现上述的配置

 

 

字符串串联

开发人员最常问的问题是我如何作字符拼接?在java中是使用号来完成的。

VTL里要想实现同样的功能你只需要将需要联合的reference放到一起就行了。例如:

      #set( $size = "Big" )

       #set( $name = "Ben" )

 

      The clock is $size$name.

   输出结果将是:The clock is BigBen.。更有趣的情况是:

      #set( $size = "Big" )

      #set( $name = "Ben" )

 

      #set($clock = "$size$name" )

 

      The clock is $clock.

   

上例也会得到同样的结果。最后一个例子,当你希望混合固定字段到你的reference时,你需要使用标准格式:

      #set( $size = "Big" )

      #set( $name = "Ben" )

 

      #set($clock = "${size}Tall$name" )

 

      The clock is $clock.

   

输出结果是:The clock is BigTallBen.。使用这种格式主要是为了使得$size不被解释为$sizeTall

 

 

反馈

         如果你在手册中遇到任何错误或者有其他关于Velocity用户手册的反馈,请email Velocity  user list。谢谢!

[C#]NVelocity介绍

mikel阅读(1178)

nVelocity是一个基于.NET的模板引擎(template engine)。它允许任何人仅仅简单的使用模板语言(template language)来引用由.NET代码定义的对象。

nVelocity 应用于web开发时,界面设计人员可以和.NET程序开发人员同步开发一个遵循MVC架构的web站点,也就是说,页面设计人员可以只关注页面的显示效果,而由.NET程序开发人员关注业务逻辑编码。nVelocity.NET代码从web页面中分离出来,这样为web站点的长期维护提供了便利,同时也为我们在aspx之外又提供了一种可选的方案。

nVelocity的能力远不止web站点开发这个领域,例如,它可以从模板(template)产生SQLPostScriptXML,它也可以被当作一个独立工具来产生源代码和报告,或者作为其他系统的集成组件使用。nVelocity也可以为很多web开发架构提供模板服务(template service)。我们的系统就提供一个模板服务的方式允许一个web应用以一个真正的MVC模型进行开发。

1.2 nVelocity能为我们作什么?

假设你是一家专门出售Mud的在线商店的页面设计人员,让我们暂且称它为“在线MUD商店”。你们的业务很旺,客户下了各种类型和数量的Mud订单。他们都是通过输入用户名和密码后才登陆到你的网站,登陆后就允许他们查看订单并购买更多的Mud。现在,一种非常流行的mud正在打折销售。另外有一些客户规律性的购买另外一种也打折但不是很流行的Bright Red Mud,由于购买的人并不多所以它被安置在页面的边缘。所有用户的信息都是被跟踪并存放于数据库中的,所以某天有一个问题可能会冒出来:为什么不使用nVelocity来使用户更好的浏览他们感兴趣的商品呢? 

nVelocity使得web页面的客户化工作非常容易。作为一个站点的设计人员,你希望每个用户登陆时都拥有自己的页面。 

你会见了一些公司内的软件工程师,你发现他们每个人都同意客户应该拥有具有个性化的信息。那让我们把软件工程师应该作的事情放在一边,看一看你应该作些什么吧。

你可能在页面内嵌套如下的VTLnVelocity template language)声明:

<html>
<body>
Hello $customer.Name!
<table>
#foreach( $mud in $nudsOnSpecial )
#if ( $customer.hasPurchased( $mud ) )
<tr><td>$flogger.getPromo( $mud )</td></tr>
#end
#end
</table>

1.1 VTL (nVelocity template language)

VTL意味着提供最简单、最容易并且最整洁的方式合并页面动态内容。 VTL 使用references来在站点内嵌套动态内容,一个变量就是一种类型的reference。变量是某种类型的reference,它可以指向.NET代码中的定义,或者从当前页面内定义的VTL statement得到值。下面是一个VTL statement的例子,它可以被嵌套到 HTML代码中:

#set ( $a = “Velocity” )

和所有的VTL statement一样,这个statement以#字符开始并且包含一个directiveset。当一个在线用户请求你的页面时,nVelocity Template Engine将查询整个页面以便发现所有#字符,然后确定哪些是VTL statement,哪些不需要VTL 作任何事情。 

#字符后紧跟一个directiveset时,这个set directive使用一个表达式(使用括号封闭)将一个值设置给变量。变量被列在左边,而它的值被列在右边,最后他们之间使用=号分割。

在上面的例子中,变量是$a,而它的值是Velocity。和其他的references一样以$字符开始,而值总是以双引号封闭。Velocity中仅有String可以被赋值给变量。

记住以下的规则: 

使用$字符开始的references用于得到什么;使用#字符开始的directives用于作些什么。

1.2 Hello nVelocity World

一旦某个变量被分配了一个值,那么你就可以在HTML文件的任何地方引用它。在下面的例子中,一个值被分配给$foo变量,并在其后被引用。 

<html> 

<body> 

#set ( $foo = “nVelocity” ) 

Hello $foo World! 

</body> 

</html> 

上面的实现结果是在页面上打印“Hello nVelocity World!”

为了使包含VTL directivesstatement更具有可读性,我们鼓励你在新行开始一个VTL statement,尽管你不是必须这么作。Set directive将在后面详细描述。

1.1 注释

单行注释:

 ## This is a single line comment.

多行注释: 

 #* 

   Thus begins a multi-line comment. Online visitors won’t

   see this text because the Velocity Template Engine will ignore it.

 *# 

文档格式: 

 #** 

   This is a VTL comment block and 

   may be used to store such information as the document author

and versioning information: 

   @version 5 

   @author 

 *# 

1.2 References

VTL中有三种类型的references:变量(variables)、属性(properties)、方法 (methods)。作为一个使用VTL的页面设计者,你和你的工程师必须就references的名称达成共识,以便你可以在你的template中使用它们。

VTL中一切reference可以作为一个String对象处理。如果有一个对象$foo是一个Integer对象,那么Velocity将调用它的toString()方法将这个对象转型为String类型。

1.2.1 变量

格式要求同.NET

1.1.1 属性

例子:

$customer.Address

$purchase.Total

$customer.Address有两种含义。它可以表示:查找hashtable对象customer中以Address为关键字的值;也可以表示调用customer对象的 getAddress()方法。当你的页面被请求时,Velocity将确定以上两种方式选用那种,然后返回适当的值。

1.1.2 方法

一个方法就是被定义在.NET中的一段代码,并且它有完成某些有用工作的能力,例如一个执行计算和判断条件是否成立、满足等。方法是一个由$开始并跟随VTL标识符组成的References,一般还包括一个VTL方法体。例如:

$customer.getAddress()

$purchase.getTotal()

$page.setTitle( “My Home Page” )

$person.setAttributes( [“Strange”, “Weird”, “Excited”] )

前两个例子$customer.getAddress()$purchase.getTotal()看起来挺想上面的属性$customer.Address $purchase.Total。如果你觉得他们之间有某种联系的话,那你是正确的。

VTL属性可以作为VTL方法的缩写。$customer.Address属性和使用$customer.getAddress()方法具有相同的效果。如果可能的话使用属性的方式是比较合理的。属性和方法的不同点在于你能够给一个方法指定一个参数列表。

1.1.1 正式reference标记

reference正式格式如下:

${mudSlinger}变量

${customer.Address}属性

${purchase.getTotal()}方法

正式格式更见常用,但是有时还是使用正是格式比较适合。例如:你希望通过一个变量$vice来动态的组织一个字符串。

Jack is a $vicemaniac.

本来变量是$vice现在却变成了$vicemaniac,这样Veloctiy就不知道您到底要什么了。所以,应该使用正是格式书写

Jack is a ${vice}maniac

现在Velocity知道变量是$vice而不是$vicemaniac

1.1.2 Quiet reference notation

如:

<input type=”text” name=”email” value=”$email” />

当页面的form被初始加载时,变量$email还没有值,这时你肯定是希望它能够显示一个空白来代替输出”$email”这样的字段。那么使用quiet reference notation就比较合适。

<input type=”text” name=”email” value=”$!email”/>

这样文本框的初始值就不会是email而是空值了。

正式和quiet格式的reference notation也可一同使用,像下面这样:

<input type=”text” name=”email” value=”$!{email}”/>

1.1.1 Getting literal

nVelocity使用特殊字符$#来帮助它工作,所以如果要在template里使用这些特殊字符要格外小心。本节将讨论$字符。

VTL中使用$2.5这样的货币标识是没有问题得的,VTL不会将它错认为是一个reference,因为VTL中的reference总是以一个大写或者小写的字母开始。

VTL中使用“"”作为转义字符。

例如:

#set( $email = “foo” )

$email

"$email

""$email

"""$email

将输出为:

foo

$email

"foo

""$email

如果email变量没有被定义则

$email

"$email

""$email

"""$email

将被输出为:

$email

"$email

""$email

"""$email

注意:VTL中未被定义的变量将被认为是一个字符串,所以以下例子:

#set( $foo = “gibbous” )

$moon = $foo

的输出结果是:

$moon = gibbous

现在你已经对reference比较熟悉了,你可以将他们高效的应用于你的template了。nVelocity利用了很多.NET规范以方便了设计人员的使用。例如:

$foo

$foo.getBar()

##与下面这句相同

$foo.Bar

$data.getUser(“john”)

##与下面这句相同

$data.User(“john”)

$data.getRequest().getServerName()

#与下面这句相同

$data.Request.ServerName

##与下面这句相同

${data.Request.ServerName}

但是,注意VTL中不会将reference解释为对象的实例变量。例如:$foo.Name将被解释为Foo对象的getName()方法,而不是Foo对象的Name实例变量。

1.1 Directives

Reference允许设计者使用动态的内容,而directive使得你可以应用.NET代码来控制你的显示逻辑,从而达到你所期望的显示效果。

1.1.1 赋值#set

#set directive被用于设置一个reference的值。例如:

#set ( $primate = “monkey” )

#set ( $customer.Behavior = $primate )

赋值左侧的(LHS)必须是一个变量或者属性reference。右侧(RHS)可以是以下类型中一种:

变量reference

字符串

属性reference

方法reference

数字

ArrayList

下面是应用各种类型的RHS的例子:

set ( $monkey = $bill ) ##变量reference

set ( $monkey.Friend = “monica” ) ##字符串

set ( $monkey.Blame = $whitehouse.Leak ) ##属性reference

set ( $monkey.Plan = $spindoctor.weave($web) ) ##方法reference

set ( $monkey.Number = 123 ) ##数字

set ( $monkey.Say = [“Not”, $my, “fault”] ) ##ArrayList

注意:最后一个例子的取值方法为:$monkey.Say.get(0)

RHS也可以是一个简单的算术表达式:

#set ( $value = $foo + 1 )

#set ( $value = $bar -1 )

#set ( $value = $foo * $bar )

#set ( $value = $foo / $bar )

如果你的RHS是一个nullVTL的处理将比较特殊:它将指向一个已经存在的reference,这对初学者来讲可能是比较费解的。例如:

#set ( $resut = $query.criteria(“name”) )

结果是$result

#set ( $resut = $query.criteria(“address”) )

结果是$result

如果$query.criteria(“name”)返回一个“bill”,而$query.criteria(“address”)返回的是null,则显示的结果如下:

结果是bill

结果是bill

看看下面的例子:

#set( $criteria = ["name", "address"] )

#foreach( $criterion in $criteria )

#set( $result = $query.criteria($criterion) )

#if( $result )

Query was successful

#end

#end

在上面的例子中,程序将不能智能的根据$result的值决定查询是否成功。在$result#set后(added to the context),它不能被设置回nullremoved from the context)。打印的结果将显示两次查询结果都成功了,但是实际上有一个查询是失败的。

为了解决以上问题我们可以通过预先定义的方式:

#set( $criteria = [“name”, “address”] )

#foreach( $criterion in $criteria )

#set( $result = false )

#set( $result = $query.criteria( $criterion ) )

#if( $result )

Query was successful

#end

#end

1.1.1 字符串

当你使用#set directive,字符串封闭在一对双引号内。

#set ( $directoryRoot = “www” )

#set ( $templateName = “index.vm” )

#set ( $template = “$directoryRoot/$tempateName” )

$template

上面这段代码的输出结果为:www/index.vm

但是,当字符串被封装在单引号内时,它将不被解析:

#set ( $foo = “bar” )

$foo

#set ( $blargh = ‘$foo' )

结果:

bar

$foo

上面这个特性可以通过修改Views目录下的nvelocity.properties文件的stringliterals.interpolate = false的值来改变上面的特性是否有效。

1.1.2 条件语句

if/elseif/else

当一个web页面被生成时使用Velocity#if directrive,如果条件成立的话可以在页面内嵌入文字。例如:

#if ( $foo )

<strong>nVelocity!</strong>

#end

上例中的条件语句将在以下两种条件下成立:

$foo是一个boolean型的变量,且它的值为true

$foo变量的值不为null

这里需要注意一点:Velocity context仅仅能够包含对象,所以当我们说“boolean”时实际上代表的时一个Boolean对象。即便某个方法返回的是一个boolean值,Velocity也会利用内省机制将它转换为一个Boolean的相同值。

如果条件成立,那么#if#end之间的内容将被显示。

#elseif#else元素可以同#if一同使用。例如:

#if( $foo < 10 )

<strong> Go North </strong>

#elseif( $foo == 10 )

<strong> Go East </strong>

#elseif( $foo == 6 )

<strong> Go South </strong>

#else

<strong> Go West </strong>

#end

注意这里的nVelocity的数字是作为Integer来比较的――其他类型的对象将使得条件为false,它使用==来比较两个值,而且nVelocity要求等号两边的值类型相同。