首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

制作一个自己的模板引擎(三)

2013-03-06 
打造一个自己的模板引擎(三)上一篇我们用栈的方式重新实现了渲染器,并且基本参照jstl的规范实现了tag标签,

打造一个自己的模板引擎(三)
上一篇我们用栈的方式重新实现了渲染器,并且基本参照jstl的规范实现了tag标签,目前它可以工作的很好。
现在我们要对它进行重新设计,完全抛弃之前的设计。之前的方式在性能上不太好,而且代码也显得有点复杂。
先考虑汇编是怎么运行的:
系统启动之后,cpu指针寄存器会默认指向到一个位置,记不清地址了,假设是0x800吧,然后开始执行指令, 指令是顺序存放的
执行完一条执行下一条, 假设有一个循环,起始地址是0x5000, 循环结束地址是0x5010,那么这个循环的最后一句一般是跳转到循环的开始地址。
其实就是写指针寄存器的值,就是把指针寄存器的值赋值为0x5000.
总之,cpu有一个寄存器,寄存器中存的是下一条指令的地址. cpu每次都先读这个地址,然后根据这个地址找到指令去执行。
接下来我们就用类似的方式重新设计,解析的时候仍然需要用到栈,因为节点的父子关系还需要使用栈来确立,但是其他的节点就直接放到一个list里面,
这个list就好比是一个代码段,所有的指令都存在这个list里面,包括文本和节点。
每个节点里面保存一下它的偏移地址和长度。
现在抛弃xml吧,我们新的引擎和xml再也没有关系了。用指令和指针的方式来思考这个问题。
假设有如下一个list
1. <div>                   // 文本节点
2. <c:each items"1,2,3">   // 标签节点
3. <div>hello</div>        // 文本节点
4. </c:each>
现在开始执行:
第一条指令,是个文本,直接输出,指针加1
第二条指令,是个节点,按照前篇的方式先执行doStartTag, 然后执行指针加1
第三条指令, 是个文本,直接输出,指针加1
第四条指令,是个结束节点,执行doAfterBody, 发现可以继续执行,修改指针到改节点的起始地址, 此时指针指向了2
第五条指令,此时指针指向2,因此继续指导第四条指令返回SKIP_BODY.
好了,接下来修改编译器,只需要编译出来一个list即可。任何节点都直接往list里面add即可。遇到标签节点的时候需要使用栈建立以下父子关系
父子关系可以使用一个节点引用,也可以使用一个整形变量指向这个节点的地址。
剩下的就是循环这个list,把他们当成一条条的指令执行就可以了,需要注意的是,由于我们的引擎大部分时候都运行在多线程环境下,
因此在执行过程中绝对不能对元素做任何修改,但是在运行过程中需要保存一些临时变量,例如tag的执行结果,还有tag创建了需要和节点关联,

因此需要重新创建一个新的list,每一个元素用一个新的类来表示,下面是渲染器的主要代码:

public static void execute(final List<Node> list, final PageContext pageContext){    Node node = null;    JspWriter out = null;    Statement statement = null;    JspWriter jspWriter = pageContext.getOut();    List<Statement> statements = getStatements(list);    ExpressionContext expressionContext = pageContext.getExpressionContext();    try    {        int flag = 0;        int index = 0;        int size = statements.size();        while(index < size)        {            out = pageContext.getOut();            statement = statements.get(index);            node = statement.getNode();            if(node.getNodeType() == NodeType.TEXT)            {                out.write(node.toString());                index++;                continue;            }            if(node.getNodeType() == NodeType.EXPRESSION)            {                Object value = expressionContext.evaluate(node.toString());                if(value != null)                {                    out.write(value.toString());                }                index++;                continue;            }            if(node.getLength() == 0)            {                throw new RuntimeException(toString("Exception at ", node));            }            if(node.getOffset() == index)            {                flag = doStartTag(statement, pageContext);                if(flag == Tag.SKIP_BODY)                {                    index = node.getOffset() + node.getLength();                    continue;                }                if(flag == Tag.SKIP_PAGE)                {                    break;                }            }            else            {                flag = doEndTag(statement, pageContext);                if(flag == BodyTag.EVAL_BODY_AGAIN)                {                    index = node.getOffset() + 1;                    continue;                }                if(flag == Tag.SKIP_PAGE)                {                    break;                }            }            index++;        }    }    catch(Throwable t)    {        throw new RuntimeException(toString("Exception at ", node), t);    }    pageContext.setOut(jspWriter);    try    {        jspWriter.flush();    }    catch(IOException e)    {    }}/** * @param statement * @return int */public static int doStartTag(final Statement statement, final PageContext pageContext){    Tag tag = statement.getTag();    Node node = statement.getNode();    if(tag == null)    {        tag = TagFactory.create(pageContext, node.getNodeName());        tag.setPageContext(pageContext);        statement.setTag(tag);        Statement parent = statement.getParent();        if(parent != null)        {            tag.setParent(parent.getTag());        }    }    // create - doStartTag    TagUtil.setAttributes(tag, node.getAttributes(), pageContext.getExpressionContext());    int flag = tag.doStartTag();    statement.setStartTagFlag(flag);    if(flag == Tag.SKIP_PAGE)    {        return Tag.SKIP_PAGE;    }    if(flag != Tag.SKIP_BODY)    {        if(flag != Tag.EVAL_BODY_INCLUDE)        {            if(tag instanceof BodyTag)            {                BodyTag bodyTag = (BodyTag)tag;                BodyContent bodyContent = (BodyContent)(pageContext.pushBody());                bodyTag.setBodyContent(bodyContent);                bodyTag.doInitBody();            }        }    }    return flag;}/** * @param statement * @param pageContext * @return int */private static int doEndTag(final Statement statement, final PageContext pageContext){    Tag tag = statement.getTag();    IterationTag iterationTag = null;    if(tag instanceof IterationTag)    {        iterationTag = (IterationTag)tag;    }    if(iterationTag != null)    {        int flag = iterationTag.doAfterBody();        if(flag == BodyTag.EVAL_BODY_AGAIN)        {            return flag;        }        else        {            int startTagFlag = statement.getStartTagFlag();            if(startTagFlag != Tag.SKIP_BODY)            {                if(startTagFlag != Tag.EVAL_BODY_INCLUDE)                {                    if(tag instanceof BodyTag)                    {                        pageContext.popBody();                    }                }            }        }    }    int flag = tag.doEndTag();    tag.release();    return flag;}

完整的工程源码: http://ayada.googlecode.com/svn/trunk/
也可以发邮件给我索取: xuesong.net@163.com

热点排行