您的位置:首页 > 编程语言 > Java开发

使用 JET 在 Eclipse 中创建更多更好的代码,如何掌握专家的最佳实践并提高您的模型驱动开发进度

2009-08-27 23:18 1256 查看
代码生成不是一个新概念。它出现有一段时间了,而且作为提高生产力的一种方式,随着模型驱动开发(MDD)的发展而普及开来。Eclipse
项目有一个称为 JET 的技术项目就是一个专门的代码生成器。JET 所能生成的不仅仅是 “代码”,无论如何,在本文中我们称这些非编码的物件为工件(artifiact)



JET?EMF?JET2?

JET
新用户需要知道的一件重要事情是这里所讨论的 JET 版本是指 JET 的最新版本,通常是 JET2。在 Eclipse Modeling
Framework(EMF)项目中 EMF 还使用一个较旧的 JET 版本来生成代码。JET2 是最新版本,也是新的 Eclipse
Modeling Framework Technology(EMFT)项目的一部分。如果您对旧版本的 JET 感兴趣,请参阅 参考资料
开始

在本节中,我们将介绍 JET 项目设置基础,讨论 JET 项目的结构,然后运行一个快速转换。

创建一个 JET 项目

在实际接触 JET 之前,我们需要创建一个项目。使用创建项目的标准 Eclipse 方法来实现。就 JET 来说,使用命令 File > New > Other > EMFT JET Transformations > EMFT JET Transformation Project
(请参见图 1)创建一个 EMFT JET Transformation Project。

图 1. JET 项目向导



JET 项目结构

让我们分析项目结构来搞清楚 JET 是如何工作的。在上面的部分,我们创建了一个 JET 项目(参见图 2)。在该 JET 项目中,我们需要浏览六个文件。

图 2. JET 项目结构



[b]Eclipse 项目文件(MANIFEST.MF, plugin.xml, build.properties)
[/b]


是使用 Eclipse 项目时所创建的标准文件。对于这些文件需要注意的一件重要事情是:在 plugin.xml 中,JET 将自动添加
org.eclipse.jet.transform。通过扩展该扩展点,我们让 JET 知道我们在提供一个 JET 转换。
[b]控制和模板文件(dump.jet, main.jet)
[/b]

这是在转换中所使用的模板和控制文件。将在下面的概念部分讨论其细节。
[b]输入模型(sample.xml)
[/b]

这里我们可以看到用于转换的一个示例输入文件。注意该输入可以来自任何源,并不限于项目。


更改启动模板

默认情况下,JET 定义启动模板为 main.jet。该选项可在 plugin.xml(org.eclipse.jet.transform extension)中的
startTemplate

属性下配置。在该扩展下还有其他配置选项,可随意浏览。
运行 JET 转换

一旦有了一个项目,拥有合适的模板、控制文件和一个输入模型,我们就可以运行转换。通过熟悉的 Eclipse 概念 —— 启动配置(参见图 3),JET 提供了一个方便的方式来调用转换。要访问 JET 启动配置,我们转到 Run > JET Transformation
,填充合适的选项,然后单击 Run


图 3. JET 启动配置



概念

JET 是指定模板输出工件的语言。实现一些应用程序的模板集合被称为蓝图(blueprint)
(用我们的术语)。JET 范例可以用下列等式表示:

参数 + 蓝图 = 所需的工件

蓝图是由 JET 创建的,而参数是由蓝图用户提供的。蓝图由三个不同的文件集组成:

[b]1. 参数
[/b]

用于蓝图的参数使用 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
>

蓝图将转换这些输入参数为实现该网络嗅探器的代码。蓝图的参数可视为自定义编程语言,而蓝图扮演 “编译器” 的角色,将输入转换为本机工件。
[b]2. 控制文件
[/b]

这些文件控制代码生成的执行。控制标记中最重要的标记是
<ws:file>

,它将执行一个模板并将结果转储至指定的文件。代码生成执行从 main.jet 开始,这与程序的 main 函数类似。
[b]3. 模板文件
[/b]

模板文件指定如何以及何种情况下生成文本。该文本可以是代码、配置文件或其他。


更多有关 XPath 的信息

如果 XPath 对于您是个陌生概念,请查看 XPath 参考资料


XPath

既然任何 JET 蓝图的输入都是一个 XML 模型,XPath 语言被用来引用节点和属性。此外,在表达式里 XPath 有自己的参数使用方式,这在 JET 里使用得非常多。要点如下:

路径表达式
与文件系统路径相似。路径是由斜杠分开的一系列步(
/

)。

从左向右估算步,如果这样做,通常会下行模型树。

每步通过其名字定义树节点(尽管存在其他可能性)。

在步的结尾,步可以在方括号(
[

]

)中编写可选的过滤器条件。

初始斜杠(
/

)指示表达式开始于模型树的根。

路径表达式还可以以变量开始,变量是以美元符号(
$

)开头的名字。

关于 JET 中的 XPath ,应记住以下几个要点:

变量是由几个 JET 标记定义的 - 注意
var

属性。它们可能也是由
c:setVariable

标签定义的。

需要路径表达式的 JET 标签有一个 select 属性。

任何标签属性都可能包含一个动态的 XPath 表达式,是由括号(
{

}

)所包含的 XPath 表达式。

JET 标签

下例将使用下列输入模型。

清单 2. 输入模型


<
app 
middle
="Hello"
 
>



    
<
person 
name
="Chris"
 gender
="Male"
 
/>



    
<
person 
name
="Nick"
 gender
="Male"
 
/>



    
<
person 
name
="Lee"
 gender
="Male"
 
/>



    
<
person 
name
="Yasmary"
 gender
="Female"
 
/>



</
app
>

[b]
ws:file


[/b]

该标签属于蓝图的 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 引擎定义的。
[b]
c:get


[/b]

该标签将输出 XPath 表达式的结果。例如,
Pre<c:get select="/app/@middle" />Post

将输出
PreHelloPost

。注意
select

参数将使用 XPath 表达式。要在期望静态字符串的参数中使用 XPath 表达式,可以通过将表达式封装在括号(
{

}

)中来调用动态 XPath 表达式。
[b]
c:iterate


[/b]

该标签将遍历具有特定名称的节点,为每个节点执行
iterate

的主体。例如:


<
c:iterate 
select
="/app/person"
 var
="currNode"
 delimiter
=","
 
>
 



Name = 
<
c:get 
select
="$currNode/@name"
 
/>



</
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 如下所示:

清单 3. PersonClass.java.jet




class
 
<
c:get select
=
"
$currPerson/@name
"
 
/>
Person 
...






    
public
 String getName() 
...




        
return
 
"
<c:get select=
"
$currPerson
/
@name
"
 />
"




    }

1cca5





    
public
 
void
 shout() 
...
{



        System.out.println(
"
Hello!!!
"
); 



    }

 



}

Yasmary.java 的形式将如下:

清单 4. Yasmary.java




class
 YasmaryPerson 
...






    
public
 String getName() 
...
{



        
return
 
"
Yasmary
"




    }

 





    
public
 
void
 shout() 
...
{



        System.out.println(
"
Hello!!!
"
); 



    }

 



}

Lee.java 的形式如下:

清单 5. Lee.java




class
 LeePerson 
...






    
public
 String getName() 
...




        
return
 
"
Lee
"




    }

 





    
public
 
void
 shout() 
...
{



        System.out.println(
"
Hello!!!
"
); 



    }

 



}

[b]
c:choose


c:when


[/b]

这些标签允许模板根据值有条件地转储文本。例如,下列代码:

清单 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
>

将输出:

Brother


Brother


Brother


Sister


注意
c:when

标签需要
test

参数,这需要一个 XPath 表达式。既然我们要通过一个常量比较
select

参数,可用单引号 (
''''

) 包含常量。

[b]
c:set


[/b]

该标签允许模板动态更改输入模型的属性。一个例子是:在一个字符串以多个方式映射输出文本时,像
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
>

[b]
setVariable


[/b]

该标签允许模板声明和使用一个全局变量,使用 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
>
 



Number of people = 
<
c:get 
select
="$i"
 
/>
.

输出
Number of people = 4。


可以使用
get

输出变量,如上例所示。

有超过 45 个标签,这使输出文本具有强大的表现力。表现力大多源于存在条件逻辑、动态更改输入模型和控制执行流的标签。

扩展 JET

JET 是可扩展的通过使用 Eclipse 的扩展点机制。以下是 JET 提供的六个扩展点。

[b]org.eclipse.jet.tagLibraries
[/b]

该扩展点负责定义标记库。JET 已经包含四个标记库(控制、格式、工作空间、Java),如果您要添加自己的标签功能,可从这里入手。
[b]org.eclipse.jet.xpathFunctions
[/b]

这允许在 JET XPath 执行时启用自定义 XPath 表达式。一个 JET 中这样的例子是:通过扩展该扩展点,在 XPath 表达式中使用 camelcase(参见 JET 源代码中的 CamelCaseFunction)。
[b]org.eclipse.jet.transform
[/b]

用于声明您的插件在提供 JET 转换。这是更改您使用什么来启动模板(取代 main.jet)的位置。
[b]org.eclipse.jet.modelInspectors
[/b]

这允许您定义检查器,使得 JET XPath 引擎来将加载的 Java 对象解释为 XPath 节点。检查器是将对象适配为 XPath 信息模型。作为例子,JET 使用一个模型来浏览 Eclipse 工作空间。注意这是临时 API,并可能随时间而发生变化。
[b]org.eclipse.jet.modelLoaders
[/b]

这允许您定义 JET 转换和从文件系统加载的 JET
<c:load>

标签以怎样的方式使用模型。作为示例,JET 提供模型加载器 loader org.eclipse.jet.resource,将加载 Eclipse IResource(文件,文件夹或项目)并允许通过该资源导航 Eclipse 工作空间。
[b]org.eclipse.jet.deployTransforms
[/b]

这允许您来将一个 JET 转换打包为一个用于简单发行的插件(包)。这可以被 UI 用来查看哪些转换可用。
实例:编写代码来生成代码

下列实例是一个模板,用于创建拥有任意数量属性的类。每个属性将有 getter 和 setter 与之关联,还有一些初始值。此外,所调用的函数的名称将输出到命令行,通过这种方式,模板即可为各函数添加简单的日志。

清单 9. 属性模板


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"
 
>



    public void set
<
c:get 
select
=



    
"camelCase($p/@name)" 
/>
(
<
c:get 
select
="$p/@type"
 
/>



    
<
c:get 
select
="$p/@name"
 
/>
) {



        System.out.println



        ("In set
<
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("In get
<
c:get 
select
="camelCase($p/@name)"
 
/>
()");



        return 
<
c:get 
select
="$p/@name"
 
/>
;



    }



    



</
c:iterate
>



}
这里是该模板的输入模型实例:

清单 10. 输入参数


<
app 
class
="Car"
>



    
<
property 
name
="model"
 type
="String"
 initial
="Honda Accord"
 
/>



    
<
property 
name
="horsepower"
 type
="int"
 initial
="140"
 
/>



    
<
property 
name
="spareTires"
 type
="boolean"
 initial
="true"
 
/>



</
app
>
这些输入参数生成如下类:

清单 11. 生成的类




class
 Car 
...
{



    
private
 String model;



    
private
 
int
 horsepower;



    
private
 
boolean
 spareTires;







    
public
 Car() 
...
{



        
this
.model 
=
 
"
Honda Accord
"
;



        
this
.horsepower 
=
 
140
;



        
this
.spareTires 
=
 
true
;



    }







    
public
 
void
 setModel(String model) 
...
{



        System.out.println(
"
In setModel()
"
);



        
this
.model 
=
 model;



    }



    





    
public
 String getModel() 
...
{



        System.out.println(
"
In getModel()
"
);



        
return
 model;



    }



    





    
public
 
void
 setHorsepower(
int
 horsepower) 
...
{



        System.out.println(
"
In setHorsepower()
"
);



        
this
.horsepower 
=
 horsepower;



    }



    





    
public
 
int
 getHorsepower() 
...
{



        System.out.println(
"
In getHorsepower()
"
);



        
return
 horsepower;



    }



    





    
public
 
void
 setSparetires(
boolean
 spareTires) 
...
{



        System.out.println(
"
In setSparetires()
"
);



        
this
.spareTires 
=
 spareTires;



    }



    





    
public
 
boolean
 getSparetires() 
...
{



        System.out.println(
"
In getSparetires()
"
);



        
return
 spareTires;



    }



    



}

实例:编写处理代码

为强调 JET 不仅仅可用来生成代码,我们给出了下面这个实例,这是一个模板,生成具有不同语气的电子邮件消息。所生成的各电子邮件的目的是是向某人索要求各种东西。下面提供控制文件(main.jet)及其调用的模板(email.jet)。

清单 12. main.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
>
清单 13. email.jet


<
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''"
>
My dear
</
c:when
>



    
<
c:when 
test
="''neutral''"
>
Dear
</
c:when
>



    
<
c:when 
test
="''angry''"
>
My enemy
</
c:when
>



</
c:choose
>
 
<
c:get 
select
="$currEmail/@to"
 
/>
,





I am writing you 
<
c:choose 
select
="$currEmail/@mood"
 
>



<
c:when 
test
="''happy''"
>
in joy 
</
c:when
>



<
c:when 
test
="''neutral''"
></
c:when
>



<
c:when 
test
="''angry''"
>
in burning anger 
</
c:when
>



</
c:choose
>
to ask for 
<
c:choose 
select
="$currEmail/@numItems"
 
>



<
c:when 
test
="1"
>




<
c:get 
select
="$currEmail/request/@item"
 
/>




</
c:when
>



<
c:otherwise
>



the following:





<
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''"
>
Either suffer my wrath, or
</
c:when
>



</
c:choose
>
 send me 
<
c:choose 
select
="$currEmail/@numItems"
>



<
c:when 
test
="1"
>



this item
</
c:when
>



<
c:otherwise
>



these items
</
c:otherwise
>



</
c:choose
>
 
<
c:choose 
select
="$currEmail/@mood"
 
>



<
c:when 
test
="''happy''"
>
at your earliest convenience.
</
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''"
>
Your friend,
</
c:when
>



<
c:when 
test
="''neutral''"
>
Sincerely,
</
c:when
>



<
c:when 
test
="''angry''"
>
In rage,
</
c:when
>



</
c:choose
>





<
c:get 
select
="/app/@from"
 
/>
该模板的输入模型实例如下:

清单 14. sample.xml


<
app 
from
="Nathan"
 
>



    
<
email 
to
="Chris"
 mood
="angry"
 
>



        
<
request 
item
="well-written article"
 
/>



    
</
email
>



    
<
email 
to
="Nick"
 mood
="happy"
 
>



        
<
request 
item
="Piano"
 
/>



        
<
request 
item
="Lollipop"
 
/>



        
<
request 
item
="Blank DVDs"
 
/>



    
</
email
>



</
app
>
mood

电子邮件蓝图应用于这些参数,生成下列两个文件。

清单 15. Chris.txt


My enemy Chris
,





I am writing you in burning anger to ask 
for
 a well-written article
.
 



Either suffer my wrath
,
 or send me this item immediately!





In rage
,



Nathan
清单 16. Nick.txt


My dear Nick
,





I am writing you in joy to ask 
for
 the following:





1
.
 Piano



2
.
 Lollipop



3
.
 Blank DVDs





Please send me these items at your earliest convenience
.





Your friend
,



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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐