2008年10月12日 星期日

[Displaytag] Decorator,裝飾器

如果你用了 displaytag 一段時間後,就會發現,他真的很好用!不過,有時候看起來彈性好像不夠。例如我希望某一個 column 的資料能夠有個 hyper link 到某一頁面,不過我需要傳送的 URL 中需要包含超過一個以上的 parameters,那我該怎麼辦?因為 displaytag column tag 只有提供一組 parameter 可以讓我們指定傳送的資料。

這問題的解法有很多種,目前我是使用過兩種。

第一種方法就是指定 displaytag table tag 中的 uid attribute,該 attribute 是用來讓我們 assign a variable 對於每一行的 row data,那這樣又怎樣勒?這樣就可以讓我們在外部使用 EL 的方式來取得該 row data 中的所有 column data 囉!到這裡應該可以瞭解吧!總而言之,如果我們指定 uid = "obj",那我們就可以在我們的程式中這樣用:

<display:table uid="obj" ...>

    <display:column ...>

        <a href="/Test.do?a=${obj.a}&b=${obj.b}" >link</a>

    </display:column>

</display:table>

第二種方法就是使用 displaytag 所提供的 decorator,我們可以建立一個 class extends TableDecorator,這樣我們就可以在 displaytag 的 table tag 中指定 decorator attribute 到我們剛剛建立的 class。

並且我們在此 class 中撰寫一個 method,此 method 需要遵守 JavaBean 的命名規則。

public String getLink() {

    MyObj obj = (MyObj) super.getCurrentRowObject();

    return "<a href=\"/Test.do?a="+obj.getA()+"&b="+obj.getB()+"\">";

}

上面程式中的第一行中,假設我們傳送給 displaytag 的物件是一個由 MyObj 物件所組成的 List 物件,所以每次 displaytag 在執行到 column tag 時,會迭代此 List 物件,並取出 MyObj 物件,所以我們的 getLink() method 可以藉由呼叫 super.getCurrentRowObject() method 來取得目前正迭代到的 MyObj 物件。因為 super.getCurrentRowObject() method 是回傳 Object 物件,所以我們需要作一次的 Casting。接著有了目前正迭代的物件,我們就可以在程式中拼湊出我們所需要的連接網址,也就是範例中的 anchor tag,當然,如果您想得更快,您也可以在這裡拼湊出一段 javascript 傳送到前端。

後端的程式完成後,我們要如何讓此 method 與前端接軌呢?除了指定 displaytag 的 table tag 中的 decorator attribute 外,我們還需要對相對應的 column 指定他的 property 為 link,也就是如下:

<display:column property="link" ... />

超簡單的!這樣的方式可以很輕易的 reuse,也就是在其他 displaytag 的頁面中我們可以指定某個 column 的 property 為 link,就可以為該頁面提供一個同樣的連結。

不過,如果你只是想要純粹將該 column 的 value 作一個顯示上不一樣的樣式話,例如數字要顯示出千分位符號。那你就不需要使用 table decorator,也就是你不需要使用方才我們所建立的 class,你可以改用 DisplayColumnDecorator,這不是一個 class 而是一個 interface,所以我們要改建立一個 class implements DisplayColumnDecorator。要產生這樣的 class,我們當然需要 implement 一個

decorate(java.lang.Object columnValue, javax.servlet.jsp.PageContext pageContext, MediaTypeEnum media)

method,因為這是官方的 API 中的規定。由此 method 的 parameters 中我們可以發現,我們可以直接取得此 column 在前端中指定的 value,而且我們可以取得 JSP 中 page scope 的物件,也就是我們可以在前端將資料儲存在 page scope,到了此 column decorator 我們就可以再次取回某些儲存在 page scope 中的資料。另外的 media 是可以得知 export 的型態。

所以由 parameter 我們可以著手進行我們的包裝作業了。我們在 decorate method 中將 columnValue 先轉換成 int 在透過 DecimalFormat class 進行數字的千分位符號加入:

DecimalFormat df = new DecimalFormat("###,###");
return df.format((int) columnValue);

同樣的,後端的程式完成後,前端我們就在我們需要進行包裝的 column 中指定 decorator attribute 到剛剛的 class 就可以了!

同樣的,使用 decorator 可以達到 reuse 的效果,讓系統在維護上更加的方便。好方法與大家分享之~

2008年10月9日 星期四

[Eclipse Plug-in] Jadclipse

會不會經常在用 Eclipse 時,發現錯誤訊息出現在 console 時,找半天就是沒有看到自己寫的 class 出現在錯誤中勒?反倒是看到一堆 third-party library 或是 jdk 的 classes。我想你如果經常使用 Eclipse 在開發系統,一定會常常碰到這樣的情形。很巧,今天推薦一個 Eclipse 的 Plug-in 叫做 Jadclipse,他原本就是要作 Jad (Java Decompiler),顧名思義,就是幫助我們將程式反組譯。

Jadclipse 的官方網站

如果你是用 Eclipse Europa(3.3 版),你就要下載 Jadclipse 3.3 的 plug-in,並且將下載的 .jar 檔案放到 Eclipse 資料夾底下的 plugin 資料夾中。然後使用 console 執行 eclipse.exe -clean 指令,這道指令會告知 Eclipse 去偵測是否有新的 plug-in 被加入 plugin 資料夾中。接著就要在下載 Jad 主程式 下載好的程式直接將其解壓縮,並且放到一個未來不會被誤刪的資料夾中(如:Program Files 資料夾底下),接著我們就要在 Eclipse 的 Preference 中設定。

如下圖一:

image

圖一 開啟 Preference 的方式

接著就會開啟 Preference 的設定視窗,在視窗中我們要找到 Java 項目底下的 JadClipse,並且我們設定其中的 Path to Decompile 項目的值為 剛剛安裝的 Jad.exe 的檔案位置。如下圖二:

image

圖二 JadClipse 設定 Path to Decompile 視窗

最後我們要設定檔案的關聯性,因為我們要將所有的 .class 檔案都交給 Jadclipse 去幫我們反組譯,所以我們在一樣的 Preference 視窗中的 General 項目下的 Editors 的 File Associations 中找到 *.class 預設給 JadClipse 開啟。如下圖三:

image

圖三 設定檔案關聯性視窗

設定完成後,以後就可以開啟 .class 檔案而不會是一堆不能開啟的檔案了!

好工具與大家分享之~

2008年10月8日 星期三

[SQL] Table Rotation(資料表旋轉)

看到主題你多少會感到很酷!因為 table 也可以旋轉喔?那麼神奇~

沒錯,如果你經常對於 table 要下 query,偏偏 table 又不是你設計(如果是你設計的那就活該啦XD),你可能會看到項這種的 table:

id type value
王大名 A 100
王大名 B 50
李大砲 A 40
李大砲 C 60
陳一二 D 20

 

這時候你想要整理出一個 table 如下:

id A B C D
王大名 100 50 NULL NULL
李大砲 40 NULL 60 NULL
陳一二 NULL NULL NULL 20

 

這時候你就會需要 table rotation 囉!有沒有一點感覺啦~由上面的 table 要改成下面的 table,就好像資料表做了旋轉的感覺。

回歸到 SQL 語法層面,我們該怎樣去作勒?若你是用 MS SQL 2005 那你可以鬆一口氣了!因為他有新的語法可以支援這樣的 table rotation - PIVOT and UNPIVOT。不過這裡不討論這兩種語法,我們要用最純粹的、最傳統、最正港的 SQL 語法來兜。

先假設第一個 table 就做 table Test,所以我們要取出成第二個 table 的樣式,所以我們在 SELECT 中一定會出現 id,A,B,C,D 的東西:

SELECT id,A,B,C,D

FROM test

因為 id 本來舊屬於 test Table 的資料,所以我們的目標就會在 A,B,C,D 這四個 column。如果我們要取出 column A 那我們就是要去取 test Table 中 type=A 的資料,所以目標很清楚,我們要在上面的 SQL 中在加入 sub-select:

SELECT id,

  A=(SELECT value FROM test WHERE type='A'),...

FROM test

這樣看似完成了,不過還是差一點,因為我們並沒有指定說是要誰的 A 值!所以要將其改成

SELECT T.id,

  A = (SELECT value FROM test WHERE type='A' AND id=T.id),

  B = (SELECT value FROM test WHERE type='B' AND id=T.id),..

FROM test AS T

GROUP BY T.id

哇!這樣就完成了~其實思考的過程並不難,重點是要知道 sub-select 的運用。

[Displaytag] 數字排序,外部排序

Displaytag 的確可以讓我們省下分頁功能的撰寫,並且透過輕鬆簡單的設定就可以完成。

不過,當你面對到數字的排序時,你就會發現頭大了!為甚麼 850 會比 1001 大?這下問題嚴重了,有經驗的人一看就知道這是因為 displaytag 排序的方式是由左至右比較編碼(我也不知道是用 ASCII 還是哪種方式),總之,排序出來的結果就不是我們要的!

首先我們需要先建立一個新的 class 他主要是用來比較出真正的數值,而不是用編碼的方式來比較,那為什麼要比較勒?很簡單!因為所有的排序都是兩兩比較的阿!既然需要比較,那我們就應該要 implement Comparator,而且,在JDK 5.0 以後的環境,我們有了 Generic 可以使用,所以我們就設定我們要 implement Comparator<String>。那又為什麼是要用 String 呢?因為當初會排序失敗,是因為 displaytag 是直接用字串來比較排序的!所以就用 String 囉~ 很直觀吧!

我們完成的 class 如下:

public class NumberComparator implements Comparator<String>
{

    public int compare(String arg0, String arg1)
    {
        Double num1 = Double.parseDouble(arg0);
        Double num2 = Double.parseDouble(arg1);
        return num1.compareTo(num2);
    }

}

在這裡我們因為 implement Comparator interface,所以我們要實做出 compare() method,那因為我們是要作數字的比較,我們就將傳入的兩個字串parameter 先轉換成 Double (這裡不用 Integer 是怕數字的位數不夠,乾脆就一勞永逸)。

Then? 看到這裡你一定會這樣問,好像很直觀的想法到這裡就卡住了!

別急,我們還沒找過 displaytag 的 tags 與 attributes。看看 tag reference 我們就可以發現,display:column tag 中有個 attribute 為 comparator,並且從描述中就可以瞭解到此 attribute 是用來讓我們可以增加自己的 comparator,所以我們就將我們剛剛傳寫好的 NumberComparator 指定給要使用數字排序的 column。這樣就大功告成~ 收工!~