Chapter 6 Mobile Application Transformation
6.3 J2ME Transformation Pattern
在本轉換樣式(transformation pattern)中,要說明的是:將 PUML 文件轉換成 J2ME MIDP 程式碼的轉換規則,以及將 PGML 文件轉換成 J2ME MIDP 程式的 轉換規則。以上兩種轉換規則合稱J2ME Transformation Pattern。
使用PUML / PGML 所描述的行動應用程式,經過轉換成 J2ME MIDP 程式碼之 後,所有轉換出來的.class,都必須在同個 package 下(即在同一個資料夾下)。
A. PUML 轉成 J2ME MIDP 的轉換規則
接下來以條例式的方式,列出PUML 轉成 J2ME MIDP 的轉換規則。
z 一份 PUML 文件轉換成相對應的 J2ME MIDP 之後,會產生兩種類型的 class,以及一個 interface。轉換系統會產生一個 interface –
BMngr_Interface。<user-interface>會對應到一個繼承自 MIDlet 的 class,且此 class 必須實作轉換系統所產生的 BMngr_Interface interface (extends MIDlet implements BMngr_Interface)。每個<board>會對應到一 個繼承自 Form 並實做 CommandListener interface 的 class (extends Form implements CommandListener)。
z BMngr_Interface interface 有兩個 method 需要被實做(implementation)。
一個是
changeBoard(String boardName) method,透過參數傳遞,指出
所要切換的使用者介面,並在此method 被執行之後,畫面切換到所指 定的使用者介面。另一個是
getBoard(String boardName) method,透過參數傳遞,指出所
要取得的使用者介面,並在此method 被執行之後,可以取得代表該使 用者介面的變數,可以對此變數作操作,例如:更改該使用者介面中的,某個使用者介面元件的狀態值。
interface BMngr_Interface {
public void changeBoard(String boardName);
public Displayable getBoard(String boardName);
}
[圖 6-3 BMngr_Interface interface]
z 將一份 PUML 文件做轉換之後,只產生一份.java 檔,裡頭包含了所有 的 class(第 1 點所提到的兩種 class),以及 BMngr_Interface 這個 interface。在這份.java 檔中:
a) <user-interface>所對應的 class(即繼承 MIDlet 的 class),必須為 main class。
b) main class 必須是 public class,其他的 class、interface 不使用 public 這個關鍵字。也就是說,在同一份.java 檔中的所有 class 們,僅能 有一個class 是 public,且 interface 不能是 public。
c) .java 檔的檔名要跟 main class 的 class name 一樣。
z 在轉換成 J2ME MIDP 程式碼之後,<user-interface> element 會對應到一 個繼承自MIDlet 的 class,且該 class 實作轉換系統產生的一個 interface – BMngr_Interface 。 <user-interface> 所 對 應 到 的 class 名 稱 為 : BMngr_@user-interfaceName。這裡的@user-interfaceName 指的是
<user-interface>的 attribute – name 的屬性值。
z 在轉換成 J2ME MIDP 程式碼之後,<board> element 會對應到一個繼承 自 Form 的 class,且該 class 實作一個 interface – CommandListener。
<board> 所 對 應 到 的 class 名 稱 為 : B_@boardName 。 這 裡 的
@boardName 指的是<board>的 attribute – name 的屬性值。
z 在 BMngr_@user-interfaceName class 中:
a) 在 PUML 文件中描述宣告的 global logic object 所對應的 class,在轉 換 成 J2ME MIDP 程 式 碼 之 後 , 必 須 在 此 BMngr_@user-interfaceName class 中,一開始就被宣告並產生物件 出來(使用 new 這個關鍵字),並且對外開放(使用 public 這個關鍵 字)。由於邏輯物件(logic object)對應的 class,其建構子(constructor) 不需要參數,所以在產生物件的過程中,不需給與初始值。
[例如]
在PUML 文件中有一個 global logic object 的描述宣告為:
<object name="userInfo" source="UserInfo.pgml"/>。
這個描述相對應的程式碼為:
public UserInfo lgObj_userInfo = new UserInfo();
b) logic object 的變數名要加上〝lgObj_〞這個前置敘述(prefix),用來 表示此變數代表的是一個logic object。即 lgObj_@objectName,這 裡的@objectName 指的是相對應的<object>的 attribute – name 的屬
性值。
c) 在 PUML 文件中描述的<board>所對應的 class,在轉換成 J2ME MIDP 程式碼之後,必須在此 BMngr_@user-interfaceName class 中,一開始就被宣告並產生物件出來(使用 new 這個關鍵字),但不 對外開放(使用 private 這個關鍵字)。由於<board>對應的 class,其 建構子(constructor)不需要參數,所以在產生物件的過程中,不需給 與初始值。
[例如]
在PUML 文件中有一個<board>的描述為:
<board name="welcomeBoard" title="Simple Demo">。
這個描述相對應的程式碼為:
private B_welcomeBoard welcomeBoard = new B_welcomeBoard();
d) 在 BMngr_@user-interfaceName class 中,要有下列變數宣告敘述:
public static BMngr_@user-interfaceName instance;
private Display display;
(@user-interfaceName 為<user-interface>的 attribute – name 的屬性值) e) 若在 PUML 文件中沒有特別說明的話,以第一個<board>所對應的
class,當第一個使用者介面呈現。在轉換成 J2ME MIDP 程式碼後,
以
display.setCurrent( )這個 method 來設定第一個要呈現的使用者介
面。f) 實作 BMngr_Interface interface 的 changeBoard(String boardName ) method,實作方法如下:
比較傳入的參數值,然後利用
display.setCurrent( )這個 method 來切
換所要呈現的使用者介面。在轉換過程中,會將所有的<board>所對 應的使用者介面,都列入J2ME MIDP 的程式比較中,下面是 J2ME MIDP 程式碼的片段(圖 6-4):public void changeBoard(String boardName){
if(boardName.compareTo("welcomeBoard") == 0){
display.setCurrent(welcomeBoard);
}
if(boardName.compareTo("infoBoard") == 0){
display.setCurrent(infoBoard);
}
if(boardName.compareTo("fruitBoard") == 0){
display.setCurrent(fruitBoard);
}
if(boardName.compareTo("colorsBoard") == 0){
display.setCurrent(colorsBoard);
} }
[圖 6-4 changeBoard( ) method 的 J2ME MIDP 範例程式碼片段]
g) 實 作 BMngr_Interface interface 的 getBoard(String boardName)
method,實作方法如下:
比較傳入的參數值,然後回傳參數值所指定的使用者介面的變數 (data type 為 displayable)。在轉換過程中,會將所有的<board>所對 應的使用者介面,都列入J2ME MIDP 的程式比較中,下面是 J2ME MIDP 程式碼的片段(圖 6-5):
public Displayable getBoard(String boardName){
Displayable displayable = null;
if(boardName.compareTo("welcomeBoard") == 0){
displayable = welcomeBoard;
}
if(boardName.compareTo("infoBoard") == 0){
displayable = infoBoard;
}
if(boardName.compareTo("fruitBoard") == 0){
displayable = fruitBoard;
}
if(boardName.compareTo("colorsBoard") == 0){
displayable = colorsBoard;
}
return displayable;
}
[圖 6-5 getBoard( ) method 的 J2ME MIDP 範例程式碼片段]
z 在 B_@boardName class 中:
a) 建構子(constructor)不需參數。
b) 在 PUML 文件中描述宣告的 local logic object 所對應的 class,在轉 換成J2ME MIDP 程式碼之後,必須在此 B_@boardName class 中,
一開始就被宣告並產生物件出來(使用 new 這個關鍵字),但不對外 開放(使用 private 這個關鍵字)。由於邏輯物件(logic object)對應的 class,其建構子(constructor)不需要參數,所以在產生物件的過程 中,不需給與初始值。
[例如]
在PUML 文件中有一個 local logic object 的描述宣告為:
<object name="fruitInfo" source="FruitInfo.pgml"/>。
這個描述相對應的程式碼為:
private FruitInfo lgObj_fruitInfo = new FruitInfo();
c) logic object 的變數名要加上〝lgObj_〞這個前置敘述(prefix),用來 表示此變數代表的是一個logic object。即 lgObj_@objectName,這 裡的@objectName 指的是相對應的<object>的 attribute – name 的屬 性值。
d) 在 PUML 文件中描述的使用者介面元件,在轉換成 J2ME MIDP 程 式碼之後,必須在此B_@boardName class 中,一開始就被宣告,
並且對外開放(使用 public 這個關鍵字)。
[例如]
在PUML 文件中有一個使用者介面的描述為:
<label name="helloMsg" showText="Hello, "/>。
這個描述相對應的程式碼為:
public StringItem helloMsg;
e) 在 PUML 文件中描述的<action> element,在轉換成 J2ME MIDP 程 式碼之後,必須在此B_@boardName class 中,一開始就被宣告並 產生物件出來(使用 new 這個關鍵字),但不對外開放(使用 private 這個關鍵字)。<action>對應於 Command 物件,且 command type 使 用Command.SCREEN,而 priority 設成 1。
[例如]
在PUML 文件中有一個使用者介面的描述為:
<action name="singleFruit" showText="Choose a Fruit">。
這個描述相對應的程式碼為:
private Command singleFruit =
new Command("Choose a Fruit", Command.SCREEN, 1);
f) 在建構子(constructor)中必須做的事有:
- 初始化使用者介面元件 - 設定 Command Listener
(Command Listener 即是本身 B_@boardName class,因為此 class 實作了 CommandListener interface)
- 把使用者介面元件,排放到包含它的使用者介面之中 - 在使用者介面中,加入用來驅動事件的 Command
[例如]
在PUML 文件中,使用者介面描述的片段為:
<board name="infoBoard" title="User Information">
<label name="helloMsg" showText="Hello, "/>
……
<action name="singleFruit" showText="Choose a Fruit">
……</action>
</board>
這個描述片段在建構子中,相對應的程式碼為:
public B_infoBoard( ) {
/* initialize UI component */
super("User Information");
helloMsg = new StringItem("Hello, ", "");
...
/* set up listeners */
this.setCommandListener(this);
/* append UI component */
this.append(helloMsg);
...
/* add commands */
this.addCommand(singleFruit);
...
}
z PUML 文件中的<textnote>,在轉換成 J2ME MIDP 程式碼時,對應到的 是TextField class。再產生相對應的 TextField 的物件時,預設可供使用 者輸入資料的欄位長度是50。
z 任何會使用到的圖檔,都要轉換成 png 格式(.png 檔)。
z 在轉換成 J2ME MIDP 程式碼時,要使用 PUML 文件裡,所描述的某個 使用者介面元件的狀態值時,記得要使用該使用者介面元件相對應的 class,裡頭的 method 來獲得,不要直接使用代表該使用者介面元件的 變數 (此為轉換時可能犯的錯誤)。
z 在 PUML 文件中,可能會透過<action> element content 的描述,來將
<listpaper>所代表的使用者介面元件的狀態值,交給 PUML 文件中所指 定的邏輯物件來處理。而處理的方式便是呼叫該邏輯物件相對應的class 中的method,使用者介面元件的狀態值便被當成參數,傳入該 method 之中。這個時候,必須分兩種狀況來做轉換的處理:
- 當<listpaper>的 attribute – mode 的屬性值為〝single〞時(單選) 使 用 <listpaper> 相 對 應 的 ChoiceGroup class 中 的 getSelectedIndex( ) method,來取得使用者介面元件的狀態值。
由於 PGML 文件所描述出來的邏輯物件,在處理<listpaper>所 對應到的使用者介面元件時,選單中選項的編號是從1 開始,
而在J2ME MIDP 中,選項編號是從 0 開使。所以利用所提到 的method 來取得狀態值之後,必須再加上 1,才能讓邏輯物件 來做邏輯運算的處理。
- 當<listpaper>的 attribute – mode 的屬性值為〝multi〞時(多選) 要將<listpaper>所對應的使用者介面元件的狀態值,轉化成一個
〝多選字串〞之後,才能交給邏輯物件來做邏輯運算的處理。
所謂〝多選字串〞,即是將使用者在選單中,所選擇的多個項目 的項目編號組合成一個字串,該字串稱為〝多選字串〞,例如:
使用者選擇了編號分別為 1、2、3、4 的項目,則該〝多選字串〞
便是〝1;2;3;4〞。
z 〝多選字串〞必須在做邏輯運算之前產生出來,產生多選字串的範例程 式碼如下(圖 6-6):
selectedStr = ""; //selectedStr是String
for(int i=0; i < iColors.size(); i++){ //iColors是ChoiceGroup if(iColors.isSelected(i)){
if(selectedStr != ""){
selectedStr = selectedStr + ";" + Integer.toString(i+1);
} else{
selectedStr = selectedStr + Integer.toString(i+1);
} }
}
[圖 6-6 多選字串的範例程式碼]
同樣地,由於 PGML 文件所描述出來的邏輯物件,在處理<listpaper>所 對應到的使用者介面元件時,選單中選項的編號是從1 開始,而在 J2ME MIDP 中,選項編號是從 0 開使。所以利用所提到的 method 來取得狀態 值之後,必須再加上1,才能讓邏輯物件來做邏輯運算的處理。
z 其他 J2ME MIDP 程式碼的轉換部份,則根據 J2ME MIDP 的語法,對 照著 PUML 的 element 做很直覺的轉換,可參考 appendix 中所附的 PUML 的原始碼,以及其轉換出來的 J2ME MIDP 程式碼。
B. PGML 轉成 J2ME MIDP 的轉換規則
接下來以條例式的方式,列出PGML 轉成 J2ME MIDP 的轉換規則。
z 一個 PGML 文件所描述的邏輯物件,在轉換成 J2ME MIDP 之後,會對 應到一個對外開放的class (使用 public 這個關鍵字),而該 class 的名稱 即為PGML 文件中<object>的 attribute – name 的屬性值。且這個 class 的建構子(constructor)不需任何參數。
z 這部份程式碼的轉換,並沒有特別的規則要遵守。則根據 J2ME MIDP 的語法,對照著PGML 的 element 做很直覺的轉換即可。可參考 appendix 中所附的PGML 的原始碼,以及其轉換出來的 J2ME MIDP 程式碼。