代码生成不是一个新概念。它出现有一段时间了,而且作为提高生产力的一种方式,随着模型驱动开发(MDD)的发展而普及开来。Eclipse 项目有一个称为 JET 的技术项目就是一个专门的代码生成器。JET 所能生成的不仅仅是 “代码”,无论如何,在本文中我们称这些非编码的物件为工件(artifiact)。
|
在本节中,我们将介绍 JET 项目设置基础,讨论 JET 项目的结构,然后运行一个快速转换。
在实际接触 JET 之前,我们需要创建一个项目。使用创建项目的标准 Eclipse 方法来实现。就 JET 来说,使用命令 File > New > Other > EMFT JET Transformations > EMFT JET Transformation Project(请参见图 1)创建一个 EMFT JET Transformation Project。
让我们分析项目结构来搞清楚 JET 是如何工作的。在上面的部分,我们创建了一个 JET 项目(参见图 2)。在该 JET 项目中,我们需要浏览六个文件。
-
Eclipse 项目文件(MANIFEST.MF, plugin.xml, build.properties)
- 这是使用 Eclipse 项目时所创建的标准文件。对于这些文件需要注意的一件重要事情是:在 plugin.xml 中,JET 将自动添加 org.eclipse.jet.transform。通过扩展该扩展点,我们让 JET 知道我们在提供一个 JET 转换。
-
控制和模板文件(dump.jet, main.jet)
- 这是在转换中所使用的模板和控制文件。将在下面的概念部分讨论其细节。
-
输入模型(sample.xml)
- 这里我们可以看到用于转换的一个示例输入文件。注意该输入可以来自任何源,并不限于项目。
|
一旦有了一个项目,拥有合适的模板、控制文件和一个输入模型,我们就可以运行转换。通过熟悉的 Eclipse 概念 —— 启动配置(参见图 3),JET 提供了一个方便的方式来调用转换。要访问 JET 启动配置,我们转到 Run > JET Transformation,填充合适的选项,然后单击 Run。
JET 是指定模板输出工件的语言。实现一些应用程序的模板集合被称为蓝图(blueprint)(用我们的术语)。JET 范例可以用下列等式表示:
参数 + 蓝图 = 所需的工件
蓝图是由 JET 创建的,而参数是由蓝图用户提供的。蓝图由三个不同的文件集组成:
-
1. 参数
-
用于蓝图的参数使用 XML 格式。这赋予它强大的表现力,因为 XML 允许使用层次化关系、允许每个节点具有属性。输入参数被称为
输入模型。蓝图将定义描述其期望参数的模式。例如,下面是蓝图创建网络嗅探器的输入实例:
清单 1. 网络嗅探器蓝图的输入
<
app
project
="NetworkSniffer"
>
<
sniffer
name
="sampler"
sample_probability
=".7"
>
<
logFile
name
="packet_types"
/>
<
packet
type
="TCP"
subType
="SYN"
>
<
logToFile
name
="packet_types"
/>
<
findResponse
type
="TCP"
subType
="ACK"
timeout
="1"
/>
</
packet
>
<
packet
type
="UDP"
>
<
logToFile
name
="packet_types"
/>
</
packet
>
</
sniffer
>
</
app
>
- 蓝图将转换这些输入参数为实现该网络嗅探器的代码。蓝图的参数可视为自定义编程语言,而蓝图扮演 “编译器” 的角色,将输入转换为本机工件。
-
2. 控制文件
-
这些文件控制代码生成的执行。控制标记中最重要的标记是
<ws:file>,它将执行一个模板并将结果转储至指定的文件。代码生成执行从 main.jet 开始,这与程序的 main 函数类似。
-
3. 模板文件
- 模板文件指定如何以及何种情况下生成文本。该文本可以是代码、配置文件或其他。
|
既然任何 JET 蓝图的输入都是一个 XML 模型,XPath 语言被用来引用节点和属性。此外,在表达式里 XPath 有自己的参数使用方式,这在 JET 里使用得非常多。要点如下:
- 路径表达式 与文件系统路径相似。路径是由斜杠分开的一系列步(
/)。 - 从左向右估算步,如果这样做,通常会下行模型树。
- 每步通过其名字定义树节点(尽管存在其他可能性)。
- 在步的结尾,步可以在方括号(
[和])中编写可选的过滤器条件。 - 初始斜杠(
/)指示表达式开始于模型树的根。 - 路径表达式还可以以变量开始,变量是以美元符号(
$)开头的名字。
关于 JET 中的 XPath ,应记住以下几个要点:
- 变量是由几个 JET 标记定义的 - 注意
var属性。它们可能也是由c:setVariable标签定义的。 - 需要路径表达式的 JET 标签有一个 select 属性。
- 任何标签属性都可能包含一个动态的 XPath 表达式,是由括号(
{和})所包含的 XPath 表达式。
下例将使用下列输入模型。
<
app
middle
="Hello"
>
<
person
name
="Chris"
gender
="Male"
/>
<
person
name
="Nick"
gender
="Male"
/>
<
person
name
="Lee"
gender
="Male"
/>
<
person
name
="Yasmary"
gender
="Female"
/>
</
app
>
ws:file 该标签属于蓝图的
control 部分,它初始化一个模板。例如:
<
ws:file
template
="templates/house.java.jet"
path
="{$org.eclipse.jet.resource.project.name}/house1.java"
>
-
将在输入模型上运行 house.java.jet 模板并将结果转储在 $(Project Root)/house1.java 中。
{$org.eclipse.jet.resource.project.name}是一个动态 XPath 表达式,用 org.eclipse.jet.resource.project.name 变量的值替换部分字符串。该变量是由 JET 引擎定义的。
-
该标签将输出 XPath 表达式的结果。例如,
Pre<c:get select="/app/@middle" />Post将输出PreHelloPost。注意select参数将使用 XPath 表达式。要在期望静态字符串的参数中使用 XPath 表达式,可以通过将表达式封装在括号({和})中来调用动态 XPath 表达式。
c:get
-
该标签将遍历具有特定名称的节点,为每个节点执行
iterate的主体。例如:
<
c:iterate
select
="/app/person"
var
="currNode"
delimiter
=","
>
Name=
<
c:get
select
="$currNode/@name"
/>
</
c:iterate
>
c:iterate
将输出 Name = Chris, Name = Nick, Name = Lee, Name = Yasmary。
iterate 标签通常也用于控制模板的其实标记。例如,如果要为模型中的每个人创建 Java™ 类,可使用如下代码:
<
c:iterate
select
="/app/person"
var
="currPerson"
>
<
ws:file
template
="templates/PersonClass.java.jet"
path
="{$org.eclipse.jet.resource.project.name}/{$currPerson/@name}.java"
/>
</
c:iterate
>
这将创建四个 Java 类文件:Chris.java、Nick.java、Lee.java 和 Yasmary.java。注意启动标记 path 属性中的 {$currPerson/@name} 字符串。既然 path 参数不需要 XPath 表达式(像 select 参数一样),{...} 字符告知 JET 引擎通过计算 XPath 表达式代替这部分字符串。$currPerson/@name 告诉引擎用 currPerson 节点(是定义在 iterate 标签中的变量)的 name 属性来代替其字符串。
此外,在 PersonClass.java.jet 模板中,它可以引用定义在 iterate 标签中的 currPerson 节点变量。例如,假设 PersonClass.java.jet 如下所示:
class
<
c:getselect
=
"
$currPerson/@name
"
/>
Person
...
{
publicStringgetName()...{
return"<c:getselect="$currPerson/@name"/>";
}
publicvoidshout()...{
System.out.println("Hello!!!");
}
}
Yasmary.java 的形式将如下:
class
YasmaryPerson
...
{
publicStringgetName()...{
return"Yasmary";
}
publicvoidshout()...{
System.out.println("Hello!!!");
}
}
Lee.java 的形式如下:
class
LeePerson
...
{
publicStringgetName()...{
return"Lee";
}
publicvoidshout()...{
System.out.println("Hello!!!");
}
}
c:choose 和 c:when 这些标签允许模板根据值有条件地转储文本。例如,下列代码:
清单 6. c:choose/c:when 示例
<
c:iterate
select
="/app/person"
var
="p"
>
<
c:choose
select
="$p/@gender"
>
<
c:when
test
="'Male'"
>
Brother
</
c:when
>
<
c:when
test
="'Female'"
>
Sister
</
c:when
>
</
c:choose
>
</
c:iterate
>
-
将输出:
BrotherBrotherBrotherSister注意
c:when标签需要test参数,这需要一个 XPath 表达式。既然我们要通过一个常量比较select参数,可用单引号 ('') 包含常量。
-
该标签允许模板动态更改输入模型的属性。一个例子是:在一个字符串以多个方式映射输出文本时,像
Chris可能映射到Chris、chris、ChrisClass、CHRIS_CONSTANT等。c:set将其内容设置为指定的属性。下面的例子为每个人存储名为className的属性并在名字之后简单添加词Class。
清单 7. c:set 例子
<
c:iterate
select
="/app/person"
var
="p"
>
<
c:set
select
="$p"
name
="className"
>
<
c:get
select
="$p/@name"
/>
Class
</
c:set
>
</
c:iterate
>
c:set
setVariable 该标签允许模板声明和使用一个全局变量,使用 XPath 的全部能力来在任何时候操纵该变量。例如,假设要输出在输入模型中提供了多少个 person 节点。可以利用以下代码:
清单 8. c:setVariable 示例
<
c:setVariable
select
="0"
var
="i"
/>
<
c:iterate
select
="/app/person"
var
="p"
>
<
c:setVariable
select
="$i+1"
var
="i"
/>
</
c:iterate
>
Numberofpeople=
<
c:get
select
="$i"
/>
.
-
输出
Number of people = 4。可以使用
get输出变量,如上例所示。
有超过 45 个标签,这使输出文本具有强大的表现力。表现力大多源于存在条件逻辑、动态更改输入模型和控制执行流的标签。
JET 是可扩展的通过使用 Eclipse 的扩展点机制。以下是 JET 提供的六个扩展点。
-
org.eclipse.jet.tagLibraries
- 该扩展点负责定义标记库。JET 已经包含四个标记库(控制、格式、工作空间、Java),如果您要添加自己的标签功能,可从这里入手。
-
org.eclipse.jet.xpathFunctions
- 这允许在 JET XPath 执行时启用自定义 XPath 表达式。一个 JET 中这样的例子是:通过扩展该扩展点,在 XPath 表达式中使用 camelcase(参见 JET 源代码中的 CamelCaseFunction)。
-
org.eclipse.jet.transform
- 用于声明您的插件在提供 JET 转换。这是更改您使用什么来启动模板(取代 main.jet)的位置。
-
org.eclipse.jet.modelInspectors
- 这允许您定义检查器,使得 JET XPath 引擎来将加载的 Java 对象解释为 XPath 节点。检查器是将对象适配为 XPath 信息模型。作为例子,JET 使用一个模型来浏览 Eclipse 工作空间。注意这是临时 API,并可能随时间而发生变化。
-
org.eclipse.jet.modelLoaders
-
这允许您定义 JET 转换和从文件系统加载的 JET
<c:load>标签以怎样的方式使用模型。作为示例,JET 提供模型加载器 loader org.eclipse.jet.resource,将加载 Eclipse IResource(文件,文件夹或项目)并允许通过该资源导航 Eclipse 工作空间。
-
org.eclipse.jet.deployTransforms
- 这允许您来将一个 JET 转换打包为一个用于简单发行的插件(包)。这可以被 UI 用来查看哪些转换可用。
下列实例是一个模板,用于创建拥有任意数量属性的类。每个属性将有 getter 和 setter 与之关联,还有一些初始值。此外,所调用的函数的名称将输出到命令行,通过这种方式,模板即可为各函数添加简单的日志。
class
<
c:get
select
="/app/@class"
/>
{
<
c:iterate
select
="/app/property"
var
="p"
>
private
<
c:get
select
="$p/@type"
/>
<
c:get
select
="$p/@name"
/>
;
</
c:iterate
>

public
<
c:get
select
="/app/@class"
/>
(){
<
c:iterate
select
="/app/property"
var
="p"
>
this.
<
c:get
select
="$p/@name"
/>
=
<
c:choose
select
="$p/@type"
>
<
c:when
test
="'String'"
>
"
<
c:get
select
="$p/@initial"
/>
"
</
c:when
>
<
c:otherwise
><
c:get
select
="$p/@initial"
/></
c:otherwise
>
</
c:choose
>
;
</
c:iterate
>
}
<
c:iterate
select
="/app/property"
var
="p"
>
publicvoidset
<
c:get
select
=
"camelCase($p/@name)"
/>
(
<
c:get
select
="$p/@type"
/>
<
c:get
select
="$p/@name"
/>
){
System.out.println
("Inset
<
c:get
select
=
"camelCase($p/@name)"
/>
()");
this.
<
c:get
select
="$p/@name"
/>
=
<
c:get
select
="$p/@name"
/>
;
}
public
<
c:get
select
=
"$p/@type"
/>
get
<
c:get
select
="camelCase($p/@name)"
/>
(){
System.out.println("Inget
<
c:get
select
="camelCase($p/@name)"
/>
()");
return
<
c:get
select
="$p/@name"
/>
;
}
</
c:iterate
>
}
这里是该模板的输入模型实例:
<
app
class
="Car"
>
<
property
name
="model"
type
="String"
initial
="HondaAccord"
/>
<
property
name
="horsepower"
type
="int"
initial
="140"
/>
<
property
name
="spareTires"
type
="boolean"
initial
="true"
/>
</
app
>
这些输入参数生成如下类:
class
Car
...
{
privateStringmodel;
privateinthorsepower;
privatebooleanspareTires;

publicCar()...{
this.model="HondaAccord";
this.horsepower=140;
this.spareTires=true;
}

publicvoidsetModel(Stringmodel)...{
System.out.println("InsetModel()");
this.model=model;
}

publicStringgetModel()...{
System.out.println("IngetModel()");
returnmodel;
}

publicvoidsetHorsepower(inthorsepower)...{
System.out.println("InsetHorsepower()");
this.horsepower=horsepower;
}

publicintgetHorsepower()...{
System.out.println("IngetHorsepower()");
returnhorsepower;
}

publicvoidsetSparetires(booleanspareTires)...{
System.out.println("InsetSparetires()");
this.spareTires=spareTires;
}

publicbooleangetSparetires()...{
System.out.println("IngetSparetires()");
returnspareTires;
}
}
为强调 JET 不仅仅可用来生成代码,我们给出了下面这个实例,这是一个模板,生成具有不同语气的电子邮件消息。所生成的各电子邮件的目的是是向某人索要求各种东西。下面提供控制文件(main.jet)及其调用的模板(email.jet)。
<
c:iterate
select
="/app/email"
var
="currEmail"
>
<
ws:file
template
="templates/email.jet"
path
="{$org.eclipse.jet.resource.project.name}/{$currEmail/@to}.txt"
/>
</
c:iterate
>
<
c:setVariable
var
="numItems"
select
="0"
/>
<
c:iterate
select
="$currEmail/request"
var
="r"
>
<
c:setVariable
var
="numItems"
select
="$numItems+1"
/>
</
c:iterate
>
<
c:set
select
="$currEmail"
name
="numItems"
><
c:get
select
="$numItems"
/></
c:set
>
<
c:choose
select
="$currEmail/@mood"
>
<
c:when
test
="'happy'"
>
Mydear
</
c:when
>
<
c:when
test
="'neutral'"
>
Dear
</
c:when
>
<
c:when
test
="'angry'"
>
Myenemy
</
c:when
>
</
c:choose
>
<
c:get
select
="$currEmail/@to"
/>
,
Iamwritingyou
<
c:choose
select
="$currEmail/@mood"
>
<
c:when
test
="'happy'"
>
injoy
</
c:when
>
<
c:when
test
="'neutral'"
></
c:when
>
<
c:when
test
="'angry'"
>
inburninganger
</
c:when
>
</
c:choose
>
toaskfor
<
c:choose
select
="$currEmail/@numItems"
>
<
c:when
test
="1"
>
a
<
c:get
select
="$currEmail/request/@item"
/>
.
</
c:when
>
<
c:otherwise
>
thefollowing:
<
c:setVariable
var
="i"
select
="0"
/>
<
c:iterate
select
="$currEmail/request"
var
="r"
>
<
c:setVariable
var
="i"
select
="$i+1"
/>
<
c:get
select
="$i"
/>
.
<
c:get
select
="$r/@item"
/>
</
c:iterate
>

</
c:otherwise
>
</
c:choose
>
<
c:choose
select
="$currEmail/@mood"
>
<
c:when
test
="'happy'"
>
Please
</
c:when
>
<
c:when
test
="'neutral'"
>
Please
</
c:when
>
<
c:when
test
="'angry'"
>
Eithersuffermywrath,or
</
c:when
>
</
c:choose
>
sendme
<
c:choose
select
="$currEmail/@numItems"
>
<
c:when
test
="1"
>
thisitem
</
c:when
>
<
c:otherwise
>
theseitems
</
c:otherwise
>
</
c:choose
>
<
c:choose
select
="$currEmail/@mood"
>
<
c:when
test
="'happy'"
>
atyourearliestconvenience.
</
c:when
>
<
c:when
test
="'neutral'"
>
promptly.
</
c:when
>
<
c:when
test
="'angry'"
>
immediately!
</
c:when
>
</
c:choose
>

<
c:choose
select
="$currEmail/@mood"
>
<
c:when
test
="'happy'"
>
Yourfriend,
</
c:when
>
<
c:when
test
="'neutral'"
>
Sincerely,
</
c:when
>
<
c:when
test
="'angry'"
>
Inrage,
</
c:when
>
</
c:choose
>

<
c:get
select
="/app/@from"
/>
该模板的输入模型实例如下:
<
app
from
="Nathan"
>
<
email
to
="Chris"
mood
="angry"
>
<
request
item
="well-writtenarticle"
/>
</
email
>
<
email
to
="Nick"
mood
="happy"
>
<
request
item
="Piano"
/>
<
request
item
="Lollipop"
/>
<
request
item
="BlankDVDs"
/>
</
email
>
</
app
>
将 mood 电子邮件蓝图应用于这些参数,生成下列两个文件。
MyenemyChris
,

Iamwritingyouinburningangertoask
for
awell-writtenarticle
.
Eithersuffermywrath
,
orsendmethisitemimmediately!
Inrage
,
Nathan
MydearNick
,

Iamwritingyouinjoytoask
for
thefollowing:
1
.
Piano
2
.
Lollipop
3
.
BlankDVDs
Pleasesendmetheseitemsatyourearliestconvenience
.

Yourfriend
,
Nathan
在结束之前,我们希望感谢 Paul Elder 提供了宝贵的意见。整体上来说,JET 的用途不仅限于简化代码生成。JET 是一个新的 Eclipse 技术项目,我们期待有更多的开发人员在工作中使用它。
学习
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文 。
- 在 Eclipse Modeling Framework Technologies JET project 查看更多有关 JET 的信息。
- 在 Eclipse.org 了解更多有关 Eclipse Modeling Framework Technology(EMFT)项目的信息。
- 从 developerWorks 教程 “Code generation using XSLT” 中了解更多有关使用另一种技术 —— XSLT 生成代码的信息。
- 有关更多信息,请阅读 “JET Tutorial Part 1 (Introduction to JET)”。
- 通过阅读 "JET Tutorial Part 2 (Write Code that Writes Code)",了解更多有关旧版本的 JET 高级主题。
- 通过阅读 “在 Java 中使用 DOM 和 XPath 进行有效的 XML 处理” 学习如何使用 XPath 有效地处理 XML。
- 查看 W3C XPath 1.0 规范 获得更多信息。
- 通过查看 XPath by Example,了解有关 XPath 的信息。
- 通过查看 XPath Introduction,学习更多有关 XPath 的信息。
- 访问 Planet Eclipse 了解关于 Eclipse 的活动。
- 了解更多有关 Eclipse Foundation 及其众多项目的信息。
- 请参阅 “Eclipse 平台入门” 来获得有关 Eclipse 平台非常好的介绍。
- 通过访问 IBM developerWorks 的 Eclipse 项目资源 扩展您的 Eclipse 技能。
- 访问 developerWorks 开放源码专区,获得广泛的 how-to 信息、工具和项目更新,帮助您使用开放源码技术进行开发,并将其与 IBM 产品一起使用。
- 随时关注最新的 developerWorks 技术活动和 webcast。
本文介绍了Eclipse项目中的JET代码生成器,探讨了其结构、操作流程及使用技巧。JET不仅可以生成代码,还能处理多种类型的工件。




被折叠的 条评论
为什么被折叠?



