源代码产生器需要如下文件
(1)dtd文件,该文件描述总体模型。
(2)xml文件,该文件描述信息模型。
(3)xsl文件,该文件用来产生源代码。
xml文件中描述信息模型的数据结构必须与总体模型dtd文件中定义的一致。xsl源代码产生模板从xml文件中读取信息,并产生相应源代码。
用xml和xsl产生程序源代码的技术由下列几个步骤构成(如图一所示)
(1)商务分析人员(business analyst)建立概念总体模型dtd文件。该模型规定xml文件中的数据结构。这些数据包含了xml文件中的元素名称,可能属性,以及各元素间关系。
例如,在建立图一中的总体模型第一步时,我们可能考虑此模型仅由class元素组成。每一个class应具有name属性。
<!attlist class
name nmtoken #required>
每一个class还应具有一个properties选项元素。其中一个元素的零或一势(cardinality)用?表示。
<!element class (properties?)>
properties元素是由几个property元素组成的集。其中一个元素的零或多势(cardinality)用*表示。
<!element properties (property*)>
每一个property元素应具有name属性和type属性。
<!attlist property
name nmtoken #required
type cdata #required>
上面的这些dtd语句规定了一个简化的总体模型。在后面我们会进一步对此进行扩展。
(2)商务专家(business expert)建立一个信息模型xml文件。这个文件由具体的元素、元素的属性、及子元素组成。元素的结构必须与第一步建立的dtd文件中定义的结构一致。
例如对于一个具有属性name,类型为string和属性salary,类型为double的employee类,其xml文件可以定义为
<class name="employee">
<properties>
<property name="name"
type="string">
</property>
<property name="salary"
type="double">
</property>
</properties>
</class>
(3)程序开发员(programmer)利用xsl建立源代码产生逻辑。xsl文件分为两部分。第一部分从xml文件中读取信息。第二部分利用读取的信息产生源代码。
例如如下的xsl模板对xml文件中的class元素进行操作。该模板首先插入c++关键词class,然后选择当前class元素的name属性,接着插入{,激活包含在子元素properties集中的每一个子元素property模板,最后插入}。
<xsl:template match="class">
<xsl:text>class </xsl:text>
<xsl:value-of select="@name"/>
<xsl:text>
{</xsl:text>
<xsl:for-each
select="properties/property">
<xsl:apply-templates select="."/>
</xsl:for-each>
<xsl:text>
};</xsl:text>
</xsl:template>
子元素property的xsl模板插入几个缩进空格,选择当前property元素的type属性,接着插入空格和_,选择该元素的name属性,最后插入;。
<xsl:template match="property">
<xsl:text>
</xsl:text>
<xsl:value-of select="@type"/>
<xsl:text> _</xsl:text>
<xsl:value-of select="@name"/>
<xsl:text>;</xsl:text>
</xsl:template>
最终产生的源代码如下
class employee
{
string _name;
double _salary;
};
虽然这是一个相当简单的例子,但是已经勾画出了编写一个源代码产生器的具体步骤。
4、具体实例
下面我们具体讨论一个实例。我们将按照上节的步骤来建立自己的源代码产生器。
4.1、整体模型(meta-model)
整体模型应由商务分析人员来建立。整体模型规定了模型的结构。我们的整体模型是由类构成的。这些类具有属性和相关类函数。图四是我们整体模型的uml模型。根据这个整体模型而定义的dtd文件model.dtd如清单一所示。
清单一 整体模型model.dtd
— listing 1: model.dtd — the meta-model —
<!element class (info?, dependencies?, uses?,
parents?, methods?, properties?)
>
<!attlist class
name nmtoken #required
package nmtoken #implied
>
<!element dependencies (dependency*)>
<!element dependency (#pcdata)>
<!element uses (use*)>
<!element use (#pcdata)>
<!element parents (parent*)>
<!element parent (#pcdata)>
<!attlist parent
name nmtoken #required
visibility (public | private) "public"
>
<!element methods (method*)>
<!element method (info?, return?, params*, exceptions*)>
<!attlist method
name nmtoken #required
type cdata #required
visibility (public | protected | private) "public"
modifier (virtual | static) ""
const (true | false) "false"
>
<!element params (param*)>
<!element param (info?)>
<!attlist param
name nmtoken #required
type cdata #required
default cdata #implied
>
<!element properties (property*)>
<!element property (info?)>
<!attlist property
name nmtoken #required
type cdata #required
has_get (true | false) "true"
has_set (true | false) "true"
has_data (true | false) "true"
is_unique (true | false) "false"
>
<!element info (#pcdata)>
<!entity amp "&" >
<!entity lt "<" >
<!entity gt ">" >
<!entity quot """ >
— end of listing —