2009年4月25日星期六

xsl递归

昨天晚上我们项目组的一个同学希望我将我们的配置页面的列表修改一下,他提出了两个要求:

1、在传给我们一些字符串时,要是不传东西我们,我们列表中要显示未绑定,传的字符串过长时,要显示其中的一部分,后面内容省略。

2、有一个列表的显示值的底层本应发字符串给我们的,他们却是发一个ulong的数字给我,其中从最低位到从左往右第四位,分别表示的一种状态,这样一组合就有16种情况,要我们上层去将数字转化为相应的页面显示。

第一个问题比较简单,我们在写自定义列的时候,用xpath的一个函数string-length去获取字符串的长度,然后再用<xsl:choose>判断一下就可以了,代码如下:

<xsl:choose>

<xsl:when test="string-length($str) > 14"><!--$str可以使xpath路径-->

<xsl:value-of select="substring($str,1,14)"/>

<xsl:value-of select="'...'"/>

</xsl:when>
<xsl:when test="string-length($str) = 0">

<xsl:value-of select="xpath"/>

</xsl:when>

<xsl:otherwise>

<xsl:value-of select="$str"/>

</xsl:otherwise>

</xsl:choose>


第二个问题就比较麻烦了,(要是用C,就相当简单了,可是对于我们页面上来说,还是不那么容易的)看到第二个问题,我想到的方法有两种,第一种,用js获取值,然后进行转化,在动态的赋给每一个table中的那一列数据。用js写的话,会比较容易,基本语法和C一样,在页面加载后调用的初始化函数中去初始化那些显示值,这样首先用js去存储这么多数据就是一种浪费空间,然后就是对于行数比较多的,这个也会降低速度。那么就自然的引出了第二个方法,用xsl,结合xpath的方法和它的一些逻辑函数,这个实际上也是包含两种方法的,一是用xml将16种情况的显示值全部列出来,然后到时候通过取集合中的元素的方法就可以了,我个人不太喜欢这样做,因为不通用,要是有一个状态位数更多的情况就又得修改。于是我选择的是让xml存储的是每一位所代表的状态字符,然后通过判断传上来的ulong数据,对这些数据进行组装。

所以,一开始我就决定了用最后一种方法,今天刚调试出来,我是怎么做的呢?


我刚开始试过用<xsl:variable>去存储那些ulong值,然后通过修改变量的值来处理的方法,空口说不好,写出下段代码帮助理解:

<xsl:variabe name="str" select="''"/><!--str用于存储转化后的字符-->

<xsl:variable name="num" select="."/><!--用num变量存储那个ulong型数的值-->

(xsl和xpath中没有移位这种操作)下面的逻辑用伪码表示

if($num-8>0){$num = $num-8; $str = $str+"相应字符串"}

if($num-4>0){$num = $num-4; $str = $str+"相应字符串"}

if($num-2>0){$num = $num-2; $str = $str+"相应字符串"}

if($num-1>0){$num = $num-1; $str = $str+"相应字符串"}

至此,str就保存了完整的转换后的字符串了。

想的很轻松,写成了之后,页面什么显示都没有,在调试了不少时间,发现了原来xsl中的变量是不允许改变的。这个实在是有点“没人性”,好端端的变量名,只允许一次赋值,又不能够改变,这算什么变量嘛,干脆叫宏、常量得了,^_^

这下我就晕掉了,变量竟然不能够改变,我们如何处理呢,岂不就是宣告此路不通,这时,想到了强大的call-template,apply-templates这些类似函数的东东。

竟然通过变量改变搞不定,我也来学学我们这个项目中随处可见的call-template,
apply-templates这样的东西,用递归来帮助解决问题。

先把调用代码的主体copy看看

<xsl:variable name="str">

<xsl:call-template name="findGlobleString">

<xsl:with-param name="num" select="$num"/><!--要转化的ulong型的数字-->

<xsl:with-param name="step" select="'8'"/> <!--解析开始的最高位,二进制"1000”-->

<xsl:with-param name="maxstep" select="'16'"/><!--能够解析数字的上限+1-->

</xsl:call-template>

</xsl:variable>

下面我将template用伪码写出来,便于理解,更加简洁

<xsl:template name="findGlobleString">

<xsl:param name="num"/>

<xsl:param name="step"/>

<xsl:param name="maxstep"/>


/*伪码逻辑 begin*/

if($num >= $step and $num < $maxstep)//如果num的step位上为1

{

if($step == $maxstep/2) 打印相应字符串


else if($step == $maxstep/4) 打印相应字符串

...


else if($step == 0) 输出空


if($step / 2 >0)//如果step不在最低位,继续调用自己

{

/*调用自己

传参为 1.$num-$step作为num参数传入

2.$step/2 作为step参数传入

3.$maxstep 作为maxstep参数传入

*/

}

else//step已在最低位,解析结束

{

打印空

}

}

else if($num =0 or $step =0)//解析结束标志

{

打印空

}

else if($step < $maxstep and $step>1 and $num<$maxstep)//num的$step位上数字为0,且不是最低位,$step右移一位,再次调用自己解析

{


/*调用自己

传参为 1.$num作为num参数传入

2.$step/2 作为step参数传入

3.$maxstep 作为maxstep参数传入

*/

}

else

{

打印错误信息

}


/*伪码逻辑 end*/



</xsl:template>

这样执行完这些代码之后str这个variable种存储的就是我们想要转化的字符串了。同时这个template的可扩展性也是较强的,对于位数更多的情况,直接将传入的step参数和maxstep参数做相应的修改就可以了。

上面我写的这些逻辑符都是xsl中有的,这里就不贴出源码了,要转化很容易。自己对递归也没多少认识,有更好办法的,欢迎指教!

2009-04-25


0 评论: