论坛首页 Java企业应用论坛

原创freemarker功能增强小项目freemarker-sweet,实现了类似jsp tag的宏的嵌套调用和复杂参数设置

浏览 2490 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
   发表时间:2010-12-22   最后修改:2010-12-22
freemarker的宏功能非常强大,但是也有局限性,比如不能像jsp tag一样,访问父tag的参数,不利于复杂macro的封装,闭门研究freemarker代码三天,基本实现了宏的嵌套调用问题,特分享之

比如下面这个tag嵌套,是一个对ext grid的封装:
  <g:list id="gridobj" width="100%" checkbox="true"
			keyField="NEW_ID" splitInfo="splitParam"
			rowdbclick="rowdbclick" contentHeightOffset="-100">
			<g:query>
				<g:queryItem label="名称" name="NEW_TITLE" />
				<g:queryItem label="时间(起)" itemType="datefield" name="NEW_DATE1" />
				<g:queryItem label="时间(止)" itemType="datefield" name="NEW_DATE2" />
			</g:query>
			<g:field fieldname="NEW_ID" header="编号" width="80" hidden="true" />
			<g:field fieldname="STATUS" header="图片" width="35" renderer="statusHandler"/>
			<g:field fieldname="NEW_TITLE" header="项目名称" width="230" />
            <g:field fieldname="NEW_SUMMARY" header="新闻摘要" width="300" renderer="summaryHandler"/>
			<g:field fieldname="NEW_DATE" header="时间" width="85" renderer="dateformat" />
			<g:field fieldname="IMAGE_URL" header="新闻路径" width="100" hidden="true"/>
		</g:list>

但是freemarker macro没有这个能力,在宏的嵌套body中,只能在宏中通过<#nested>来访问标签之间的文本,或者直接用参数传过去,而不能在tag之间设置tag的参数,宏的局部变量对宏tag之间的body也是隐藏的

为此我开发了两个directive
ExposeDirective
ParamDirective

ExposeDirective实现了把当前宏的macroContext导出到全局变量,同是如果发现自己被宏调用嵌套,则创建一个变量保存父宏的macroContext,达到访问父宏上下文的目的
  Macro.Context mc = env.getCurrentMacroContext();
        if (mc == null) {
            throw new TemplateException("macro context not found,this directive must be call in macro", env);
        }
        List stack = DirectiveHelper.getElementStack(env);
        Object macroCall = null, parentMacroCall = null;
        for (int i = stack.size() - 1; i > -1; i--) { //遍历当前运行时堆栈,找到当前宏的UnifiedCall
            if (stack.get(i) instanceof Macro) {
                Object call = stack.get(i - 1);
                if (macroCall == null) {
                    macroCall = call;
                } else {
                    parentMacroCall = call;
                    break;
                }
            }
        }
        DirectiveHelper.exposeMacroContext(env, macroCall, mc);//暴露当前宏local变量上下文到全局上下文
        if (parentMacroCall == null)
            return;
        Macro.Context parentMc = (Macro.Context) DirectiveHelper.getMacroContext(env, parentMacroCall);//获取父宏上下文
        if (parentMc != null)
            mc.setLocalVar(DirectiveHelper.getParam(params, "parent", "parent"), parentMc.getLocals());//在当前宏上下文中保存一个叫parent的变量,值为父宏的local变量

ParamDirective则根据当前的调用堆栈找到macroContext,从而可以访问到宏的局部变量,实现了宏的嵌套使用,并且支持复杂的参数设置

请看例子:
设置变量
<#macro outer name="ozn" age="27" sex="boy">
<@expose/>
<#nested>
</#macro>

<#macro inner parent={}>
<@expose/>
<@param name="parent.name" value="cici"/>
${parent.name}
</#macro>

<@outer><@inner/></@>


使用body来设置变量值
<@param name="addr.area">${addr.country}</@>


删除变量
<#macro outer name="ozn" age="27" sex="boy">
<@expose/>
    <#nested>
</#macro>

<#macro inner parent={}>
<@expose/>
<@param name="parent.name" action="remove"/>
${parent?keys?size}
</#macro>

<@outer><@inner/></@>


添加变量
<#macro hello name=["ozn","cici"]>
<@expose/>
<#nested>
${name?size}
</#macro>
<@hello><@param name="name" value="me" action="add"/></@>


更多例子请看代码里面的testCase

另附上覆盖率测试图

有任何问题,请不吝赐教

代码托管于:
http://code.google.com/p/freemarker-sweet/
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics