30 July 2016

本文章将介绍代码生成器ArbitraryGen的脚本模式(script mode)的使用方法。如果你还不是很了解什么是ArbitraryGen,请移步ArbitraryGen首页,上面会有ArbitraryGen的简要介绍。

点击到ArbitraryGen首页

下面就进入本文的主题了:ArbitraryGen的脚本模式的使用

首先我们需要引入ArbitraryGen到项目当中,详细操作可以移步ArbitraryGen首页,在此不再冗述。

下面说下参数配置

在本文中我们将只讲述Gradle下的参数配置,至于Eclipse下的Ant,可以参考项目中的arbitrarygen.xml文件,其它编译脚本可以参考实现。(其本质就是带上一堆的参数来调用ArbitraryGen的可执行jar文件)

arbitraryGen {
    ...
    scriptEngine {
        enable true
        format "e"
    }
}

上述配置详解:

  • scriptEngine.enable配置是否开启脚本模式
  • scriptEngine.format用于配置使用脚本引擎解析处理的文件后缀,格式参照普通模式的配置
  • 其它配置项请查看首页

数据源

目前脚本模式(script mode)下数据源只支持XML格式的数据,该脚本模式下的Parser还不支持扩展,后续再不上实现。 下面通过一个示例加以说明:(events.e文件,可以到demo项目)中查看)

    <event name="DefaultEvent" >
        <field name="name" type="String" default="null"/>
    </event>

    <event name="TestEvent"  final="true" parent="DefaultTestEvent">
        <field name="name" type="String" default="null"/>
        <field name="type" type="int" default="0"/>
        <data static="true" final="true">
            <field name="errCode" type="int"/>
        	  <field name="errType" type="int" default="0"/>
        </data>
    </event>

    <event name="XmlThirdEvent">
        <data static="true" final="true">
            <field name="errCode" type="int"/>
        	  <field name="errType" type="int" default="0"/>
        </data>
    </event>
    <event name="XmlFourthEvent">
        <data>
            <field name="errCode" type="int">
                <constant name="ERR_CODE_NORMAL" value="0"/>
            </field>
        	<field name="errType" type="int" default="0">
                <constant name="ERR_TYPE_NORMAL" value="0"/>
                <constant name="ERR_TYPE_NET" value="1"/>
            </field>
        </data>
        <CallBack>
            <field name="trigger" type="String"/>
        </CallBack>
        <result>
            <field name="result" type="String"/>
        </result>
    </event>
</Events>

如果你看过ArbitraryGen的普通模式,是不是觉得上面的数据源很熟悉呢?是的,用的几乎是一样的数据源,详细可以对比一下,稍微有些不同点。

上面的数据源文件中有几个小点需要特别说一下:

  • 根节点的package属性,是用来指定其一级子节点的对应的类的包名
  • 根节点的tag属性,是用于指定其一级节点对应的所有需要生成代码的节点类型,并且与生成代码的模板存在映射关系,tag标签里面是一个有逗号“,”分隔的列表,如果tag属性不存在,则会直接读取根节点的一级节点名列表作为tag的值
  • 根节点的delegateTag属性,用于指定什么这个资源文件对应的委派类的生成代码模板对应的tag(委派类,可能会让大家感觉很难理解,可以简单理解,就是用于收集该数据源文件对应所有生成的类的一个收集类)
  • 数据源文件中的每个单元数据都将被转成JSON格式的数据作为代码模板引擎的输入数据,其命名规则为:(1)子节点:可直接通过节点引用,如data.field;(2)属性:通过属性key前面加个下划线“_”来引用,如field._name;

代码模板

下面我先贴上生成代码的模板(文件名为event.vigor-template,也可以到demo项目中查看):

package <%=_package%>;

import cc.suitalk.event.rx.RxEvent;

public final class <%=_name%> extends RxEvent {
	public final static String ID = "AutoGenEvent.<%=_name%>";

	<% for (var i = 0; this.data && i< data.length; i++) { var field = data[i];%>
		<% if (field.constant) { %>
			<% if (field.constant.length) { %>
			<% for (var k = 0; k < field.constant.length; k++) {var constant = field.constant[k];%>
				public final static <%=field._type%> <%=constant._name%> = <%=constant._value%>;
			<%}%>
			<%} else {%>
				public final static <%=field._type%> <%=field.constant._name%> = <%=field.constant._value%>;
			<%}%>
		<%}%>
	<%}%>
	<% for (var i = 0; this.result && i < result.length; i++) { var field = result[i];%>
		<% if (field.constant) {%>
			<% if (field.constant.length) {%>
			<% for (var k = 0; k < field.constant.length; k++) {var constant = field.constant[k];%>
				public final static <%=field._type%> <%=constant._name%> = <%=constant._value%>;
			<%}%>
			<%} else {%>
				public final static <%=field._type%> <%=field.constant._name%> = <%=field.constant._value%>;
			<%}%>
		<%}%>
	<%}%>

	<% if (this.data) {%>
		<% for (var i = 0; this.data && i < data.length; i++) { var field = data[i];%>
            <% if (field.constant) { %>
                /**
                 * The value can be :
                 *
                <% if (field.constant.length) { %>
                    <% for (var k = 0; k < field.constant.length; k++) {var constant = field.constant[k];%>
                        * {@link #<%=constant._name%>};
                    <%}%>
                <%} else {%>
                    * {@link #<%=field.constant._name%>};
                <%}%>
                */
            <%}%>
			public <%=field._type%> <%=field._name%><% if (field._default) {%> = <%=field._default%><%}%>;
		<%}%>
	<%}%>

	public <%=_name%>() {
	    this.action = ID;
	}
	public <%=_name%>(Callback callback) {
		this.action = ID;
		this.callback = callback;
	}

	<% if (this.result) {%>
	public Result result = new Result();
	public final static class Result {
		<% for (var i = 0; i < result.length; i++) { var field = result[i];%>
            <% if (field.constant) { %>
                /**
                 * The value can be :
                 *
                <% if (field.constant.length) { %>
                    <% for (var k = 0; k < field.constant.length; k++) {var constant = field.constant[k];%>
                        * {@link #<%=constant._name%>};
                    <%}%>
                <%} else {%>
                    * {@link #<%=field.constant._name%>};
                <%}%>
                */
            <%}%>
			public <%=field._type%> <%=field._name%><% if (field._default) {%> = <%=field._default%><%}%>;
		<%}%>
	}
	<%}%>
}

如果你学过JSP,是不是看到这个模板代码是不是有点熟悉呢?恩,没错,模板文件中的用于区分脚本代码的标签就是<%%>,这个跟JSP中的标签还是比较像的,但是模板文件中采用的是JavaScript脚本,目前ArbitraryGen的脚本模式下只支持JavaScript脚本格式,后期将会加上可扩展接口,以支持更多的脚本语言。

在模板文件中脚本所用的的所有数据均来自原数据源文件,其命名规则上面已经提到,在此不再多说了。

模板映射关系

看到上面你可能会疑惑,这个模板是怎么与数据源对应上的呢?这里就是我们要讲的模板关系映射文件template-mapping.properties了,看到是.properties文件,应该就不用我多说之中的格式了吧!直接贴上文件的内容吧。(没接触过properties格式文件的同学可自行搜索学习哈~)

template-mapping.properties文件内容如下:

# The relationship mapping for template and tag
#
# Just read hybrids generate code engine to know more.
#
table=VigorDBItem.vigor-template
hybrid-table=VigorDBItem.hybrid-template
event=event.vigor-template

其中event这个key值对应着数据源文件中的tag标签中的值(当然,tag标签里面是一个有逗号“,”分隔的列表),如果tag属性不存在,则会直接读取根节点的一级节点名列表作为tag的值。

生成出来的代码

生成了如下几个文件:

arbirarygen-script-mode-output-code

生成的代码我就补贴了,如果你跑了demo程序,并按照上述说的步骤操作了,你会发现events.e定义的数据,并不是所有节点都被输出了,如果想知道为什么,就移步到代码模板中详细看看脚本的逻辑,其实脚本逻辑还是比较容易看懂的。如有什么问题可以交流,本文到此结束了~