程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> 使用JET在Eclipse中創建更多更好的代碼

使用JET在Eclipse中創建更多更好的代碼

編輯:關於JAVA

如何掌握專家的最佳實踐並提高您的模型驅動開發進度

簡介:創建模板來使最佳實踐代碼化(即生成工件)是一種強大的概念,能節約您無數時間和減少沉悶的編碼。本文介紹代碼生成框架 JET,這是一個 Eclipse 技術的項目。

代碼生成不是一個新概念。它出現有一段時間了,而且作為提高生產力的一種方式,隨著模型驅動開發(MDD)的發展而普及開來。Eclipse 項目有一個稱為 JET 的技術項目就是一個專門的代碼生成器。JET 所能生成的不僅僅是 “代碼”,無論如何,在本文中我們稱這些非編碼的物件為工件(artifiact)。

開始

在本節中,我們將介紹 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 項目結構

Eclipse 項目文件(MANIFEST.MF, plugin.xml, build.properties) 這是使用 Eclipse 項目時所創建的標准文件。對於這些文件需要注意的一件重要事情是:在 plugin.xml 中,JET 將自動添加 org.eclipse.jet.transform。通過擴展該擴展點,我們讓 JET 知道我們在提供一個 JET 轉換。控制和模板文件(dump.jet, main.jet) 這是在轉換中所使用的模板和控制文件。將在下面的概念部分討論其細節。輸入模型(sample.xml) 這裡我們可以看到用於轉換的一個示例輸入文件。注意該輸入可以來自任何源,並不限於項目。

運行 JET 轉換

一旦有了一個項目,擁有合適的模板、控制文件和一個輸入模型,我們就可以運行轉換。通過熟悉的 Eclipse 概念 —— 啟動配置(參見圖 3),JET 提供了一個方便的方式來調用轉換。要訪問 JET 啟動配置,我們轉到 Run > JET Transformation,填充合適的選項,然後單擊 Run。

圖 3. JET 啟動配置

概念

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. 模板文件 模板文件指定如何以及何種情況下生成文本。該文本可以是代碼、配置文件或其他。

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>

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 引擎定義的。c:get 該標簽將輸出 XPath 表達式的結果。例如,Pre<c:get select="/app/@middle" />Post 將輸出 PreHelloPost。注意 select 參數將使用 XPath 表達式。要在期望靜態字符串的參數中使用 XPath 表達式,可以通過將表達式封裝在括號({ 和 })中來調用動態 XPath 表達式。c:iterate

該標簽將遍歷具有特定名稱的節點,為每個節點執行 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" />";
  }
  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!!!");
  }
}

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>

將輸出:

Brother

Brother

Brother

Sister

注意 c:when 標簽需要 test 參數,這需要一個 XPath 表達式。既然我們要通過一個常量比較 select 參數,可用單引號 ('') 包含常量。

c:set 該標簽允許模板動態更改輸入模型的屬性。一個例子是:在一個字符串以多個方式映射輸出文本時,像 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>

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>
Number of people = <c:get select="$i" />.

輸出 Number of people = 4。

可以使用 get 輸出變量,如上例所示。

有超過 45 個標簽,這使輸出文本具有強大的表現力。表現力大多源於存在條件邏輯、動態更改輸入模型和控制執行流的標簽。

擴展 JET

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 與之關聯,還有一些初始值。此外,所調用的函數的名稱將輸出到命令行,通過這種方式,模板即可為各函數添加簡單的日志。

清單 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">
a <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 技術項目,我們期待有更多的開發人員在工作中使用它。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved