顯示具有 Java 標籤的文章。 顯示所有文章
顯示具有 Java 標籤的文章。 顯示所有文章

2011年6月22日 星期三

Java Swing - Model-View-Controller (MVC) Structure


上圖是一個用Swing刻出來的簡單GUI, 目的是更新Database設定,Mailserver 設定....等等, 這邊GUI是依照Swing 的MVC 架構做出來的, Swing 的MVC包含了以下3個:

Model :包含了資料源和所有基於對這些資料的操作
View :Model 跟UI 介面的顯示
Control:在Model和View之間起到了溝通的作用,處理用戶在View上的輸入,並轉發給Model。這樣Model和View兩者之間可以做到鬆散耦合,甚至可以彼此不知道對方,而由Controller連接起這兩個部分。

在Oracle 的 Java SE Application Design With MVC 提到完整的MVC 細節如下:


  • Model - The model represents data and the rules that govern access to and updates of this data. In enterprise software, a model often serves as a software approximation of a real-world process.





  • View - The view renders the contents of a model. It specifies exactly how the model data should be presented. If the model data changes, the view must update its presentation as needed. This can be achieved by using a push model, in which the view registers itself with the model for change notifications, or apull model, in which the view is responsible for calling the model when it needs to retrieve the most current data.





  • Controller - The controller translates the user's interactions with the view into actions that the model will perform. In a stand-alone GUI client, user interactions could be button clicks or menu selections, whereas in an enterprise web application, they appear asGET andPOST HTTP requests. Depending on the context, a controller may also select a new view -- for example, a web page of results -- to present back to the user.

    整個GUI Project 結構如下:


    實作是參考 Model-View-Controller (MVC) Structure 而來的。
    首先是Model 的部份, 以GUI的Database Setting 來說是一個TabbedPanel裡的Item , 所以Model屬性會包含了所有相關元件的資料, 在這邊就是5種屬性, driverclassname,dburl,dbusername,dbuserpwd,databasetype, 我將這些封裝成一個JavaBean, 在DataBaseSettingModel裡面操作, DataBaseSettingModel針對DbSettingObject 做setter&getter 的動作,get時會從db拿值,set時會update db的資料。


    DbSettingObject.java
    package com.main.ui.dataobject;
    
    public class DbSettingObject {
     private String driverClassName;
     private String dbUrl;
     private String dbUserName;
     private String dbUserPwd;
     private String dataBaseType;
     
     public DbSettingObject(){
      this.driverClassName = "";
      this.dbUrl = "";
      this.dbUserName = "";
      this.dbUserPwd = "";
      this.dataBaseType =  "";
      
     }
     
     public String getDriverClassName() {
      return driverClassName;
     }
     public void setDriverClassName(String driverClassName) {
      this.driverClassName = driverClassName;
     }
     public String getDbUrl() {
      return dbUrl;
     }
     public void setDbUrl(String dbUrl) {
      this.dbUrl = dbUrl;
     }
     public String getDbUserName() {
      return dbUserName;
     }
     public void setDbUserName(String dbUserName) {
      this.dbUserName = dbUserName;
     }
     public String getDbUserPwd() {
      return dbUserPwd;
     }
     public void setDbUserPwd(String dbUserPwd) {
      this.dbUserPwd = dbUserPwd;
     }
     public String getDataBaseType() {
      return dataBaseType;
     }
     public void setDataBaseType(String dataBaseType) {
      this.dataBaseType = dataBaseType;
     }
     
    
     @Override
     public String toString(){
      StringBuffer tostring = new StringBuffer();
      
      tostring.append("driverClassName = "   driverClassName "\n");
      tostring.append("dbUrl = "   dbUrl "\n");
      tostring.append("dbUserName = "   dbUserName "\n");
      tostring.append("dbUserPwd = "   dbUserPwd "\n");
      tostring.append("dataBaseType = "   dataBaseType "\n");
      
      return tostring.toString();
     }
     
    }
    


    DbSettingModel.java
    package com.main.ui.model;
    
    import java.sql.SQLException;
    
    import com.main.dao.IQueryUiObjectDao;
    import com.main.dao.QueryDatabaseSettingDao;
    import com.main.ui.dataobject.DbSettingObject;
    
    public class DbSettingModel {
     
     private DbSettingObject dbObj;
     private IQueryUiObjectDao<DbSettingObject> queryDao;
     
     public DbSettingModel(){
      queryDao = new QueryDatabaseSettingDao();
     }
     
     public DbSettingObject getDbSettingObject(){
      try {
       this.dbObj = queryDao.getDataObject();
      } catch (SQLException e) {
       dbObj = new DbSettingObject();
      }  
      return dbObj;
     }
    
    
     
     public void setDbSettingObject(DbSettingObject obj){
      try {
       queryDao.setDataObject(obj);
      } catch (SQLException e) {
      }
      this.dbObj = obj;
     }
    }
    

    再來是View 的部份, DataBase Setting 的Panel設計如下
    一個View Object 是extends JPanel ,建立的相關的UI, 取得或修改UI的顯示,view 會提供Controller 相關的method 來操作。

    DbSettingView.java
    package com.main.ui.view;
    import java.awt.event.ActionListener;
    
    import javax.swing.JButton;
    import javax.swing.JLabel;
    import javax.swing.JPanel;
    import javax.swing.JSeparator;
    import javax.swing.JTextField;
    
    import com.main.ui.dataobject.DbSettingObject;
    import com.main.ui.model.DbSettingModel;
    
    public class DbSettingView extends JPanel{
     private JLabel passwd;
     private JLabel databaseType;
     private JTextField changeDbType;
     private JTextField changeUserName;
     private JTextField changePassword;
     private JTextField changeDbUrl;
     private JTextField changeDriverClassName;
     private JButton Save;
     private JSeparator jSeparator1;
     private JTextField passwordValue;
     private JTextField userNameValue;
     private JTextField dbUrlValue;
     private JTextField dbTypeValue;
     private JTextField driverClassNameValue;
     private JLabel dbUserName;
     private JLabel dbUrl;
     private JLabel DriverClassName;
     private DbSettingModel model ;
     
     public DbSettingView(DbSettingModel imodel){
      this.model = imodel;
      initGUI();
      initTextValues();
     }
     
     private void initGUI() {
      try {
       {
        this.setPreferredSize(new java.awt.Dimension(498, 222));
        this.setRequestFocusEnabled(false);
        this.setOpaque(false);
        this.setLayout(null);
        {
         DriverClassName = new JLabel();
         this.add(DriverClassName);
         DriverClassName.setBounds(12, 12, 119, 19);
         DriverClassName.setText("Driver Class Name:");
        }
        {
         dbUrl = new JLabel();
         this.add(dbUrl);
         dbUrl.setText("DB Url :");
         dbUrl.setBounds(12, 59, 95, 15);
        }
        {
         dbUserName = new JLabel();
         this.add(dbUserName);
         dbUserName.setText("UserName :");
         dbUserName.setBounds(12, 81, 95, 15);
        }
        {
         passwd = new JLabel();
         this.add(passwd);
         passwd.setText("Password :");
         passwd.setBounds(12, 102, 95, 15);
        }
        {
         databaseType = new JLabel();
         this.add(databaseType);
         databaseType.setText("DB Type :");
         databaseType.setBounds(12, 37, 95, 15);
        }
        {
         driverClassNameValue = new JTextField();
         this.add(driverClassNameValue);
         driverClassNameValue.setBounds(143, 11, 146, 22);
         driverClassNameValue.setEnabled(false);
        }
        {
         dbTypeValue = new JTextField();
         this.add(dbTypeValue);
         dbTypeValue.setBounds(143, 34, 146, 22);
         dbTypeValue.setEnabled(false);
        }
        {
         dbUrlValue = new JTextField();
         this.add(dbUrlValue);
         dbUrlValue.setBounds(143, 56, 146, 22);
         dbUrlValue.setEnabled(false);
        }
        {
         userNameValue = new JTextField();
         this.add(userNameValue);
         userNameValue.setBounds(143, 78, 146, 22);
         userNameValue.setEnabled(false);
        }
        {
         passwordValue = new JTextField();
         this.add(passwordValue);
         passwordValue.setBounds(143, 99, 146, 22);
         passwordValue.setEnabled(false);
        }
        {
         jSeparator1 = new JSeparator();
         this.add(jSeparator1);
         jSeparator1.setBounds(12, 133, 447, 7);
        }
        {
         Save = new JButton();
         this.add(Save);
         Save.setText("Save");
         Save.setBounds(394, 146, 65, 22);
        }
        {
         changeDriverClassName = new JTextField();
         this.add(changeDriverClassName);
         changeDriverClassName.setEnabled(true);
         changeDriverClassName.setBounds(313, 11, 146, 22);
        }
        {
         changeDbUrl = new JTextField();
         this.add(changeDbUrl);
         changeDbUrl.setEnabled(true);
         changeDbUrl.setBounds(313, 56, 146, 22);
        }
        {
         changePassword = new JTextField();
         this.add(changePassword);
         changePassword.setEnabled(true);
         changePassword.setBounds(313, 99, 146, 22);
        }
        {
         changeUserName = new JTextField();
         this.add(changeUserName);
         changeUserName.setEnabled(true);
         changeUserName.setBounds(313, 78, 146, 22);
        }
        {
         changeDbType = new JTextField();
         this.add(changeDbType);
         changeDbType.setEnabled(true);
         changeDbType.setBounds(313, 34, 146, 22);
        }
       }
      } catch(Exception e) {
       e.printStackTrace();
      }
     }
     
     private void initTextValues(){
      System.out.println("init text values , dbSettingView");
      if(this.model != null){
       DbSettingObject obj = this.model.getDbSettingObject();
       this.dbTypeValue.setText(obj.getDataBaseType());
       this.dbUrlValue.setText(obj.getDbUrl());
       this.userNameValue.setText(obj.getDbUserName());
       this.passwordValue.setText(obj.getDbUserPwd());
       this.driverClassNameValue.setText(obj.getDriverClassName());
       
      }
     }
     
     public void changeSetting(DbSettingObject object){
      this.dbTypeValue.setText(object.getDataBaseType());
      this.dbUrlValue.setText(object.getDbUrl());
      this.userNameValue.setText(object.getDbUserName());
      this.passwordValue.setText(object.getDbUserPwd());
      this.driverClassNameValue.setText(object.getDriverClassName());
     }
     
     public DbSettingObject getCurrentSettingObject(){
      DbSettingObject currentObject = new DbSettingObject();
      currentObject.setDataBaseType(this.dbTypeValue.getText());
      currentObject.setDbUrl(this.dbUrlValue.getText());
      currentObject.setDbUserName(this.userNameValue.getText());
      currentObject.setDbUserPwd(this.passwordValue.getText());;
      currentObject.setDriverClassName(this.driverClassNameValue.getText());
      
      return currentObject;
     }
     
     
     public void addChangeSettingListener(ActionListener mal){
      this.Save.addActionListener(mal);
     }
     
     public JLabel getPasswd() {
      return passwd;
     }
    
     public JLabel getDatabaseType() {
      return databaseType;
     }
    
     public JTextField getChangeDbType() {
      return changeDbType;
     }
    
     public JTextField getChangeUserName() {
      return changeUserName;
     }
    
     public JTextField getChangePassword() {
      return changePassword;
     }
    
     public JTextField getChangeDbUrl() {
      return changeDbUrl;
     }
    
     public JTextField getChangeDriverClassName() {
      return changeDriverClassName;
     }
    
     public JButton getSave() {
      return Save;
     }
    
     public JSeparator getjSeparator1() {
      return jSeparator1;
     }
    
     public JTextField getPasswordValue() {
      return passwordValue;
     }
    
     public JTextField getUserNameValue() {
      return userNameValue;
     }
    
     public JTextField getDbUrlValue() {
      return dbUrlValue;
     }
    
     public JTextField getDbTypeValue() {
      return dbTypeValue;
     }
    
     public JTextField getDriverClassNameValue() {
      return driverClassNameValue;
     }
    
     public JLabel getDbUserName() {
      return dbUserName;
     }
    
     public JLabel getDbUrl() {
      return dbUrl;
     }
    
     public JLabel getDriverClassName() {
      return DriverClassName;
     } 
    }
    

    再來建立Controller , 會傳入model 跟 view, 然後統過Constructor 新增view的listener , 在此操作Model 跟view 的相關動作。

    DbSettingController.java
    package com.main.ui.controller;
    
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import com.main.ui.dataobject.DbSettingObject;
    import com.main.ui.model.DbSettingModel;
    import com.main.ui.view.DbSettingView;
    
    
    public class DbSettingController {
     private DbSettingModel model;
     private DbSettingView view;
     
     public DbSettingController(DbSettingModel iModel,DbSettingView iView ){
      this.model = iModel;
      this.view = iView;
      this.view.addChangeSettingListener(new ChangeDbSettingListener());
     }
     
     class ChangeDbSettingListener implements ActionListener {
    
      //Actionlistener for change database setting when "save" button  on click
      public void actionPerformed(ActionEvent e) {
       
       DbSettingObject newSettingobject = getNewSettingObject();   
       DbSettingObject currentSettingObj = view.getCurrentSettingObject();     
       changeNewSettingObjectValues(newSettingobject, currentSettingObj);
       
       model.setDbSettingObject(newSettingobject);//update object & database values   
       view.changeSetting(model.getDbSettingObject());//get object from database
      }
    
      private void changeNewSettingObjectValues(DbSettingObject newSettingobject,DbSettingObject currentSettingObj) {
       if(newSettingobject.getDriverClassName().length() <= 0){
        newSettingobject.setDriverClassName(currentSettingObj.getDriverClassName());
       }
       if(newSettingobject.getDbUrl().length() <= 0){
        newSettingobject.setDbUrl(currentSettingObj.getDbUrl());
       }
       if(newSettingobject.getDataBaseType().length() <= 0){
        newSettingobject.setDataBaseType(currentSettingObj.getDataBaseType());
       }
       if(newSettingobject.getDbUserName().length() <= 0){
        newSettingobject.setDbUserName(currentSettingObj.getDbUserName());
       }
       if(newSettingobject.getDbUserPwd().length() <= 0){
        newSettingobject.setDbUserPwd(currentSettingObj.getDbUserPwd());
       }
      }
    
      private DbSettingObject getNewSettingObject() {
       DbSettingObject newSettingobject = new DbSettingObject();   
       newSettingobject.setDataBaseType(view.getChangeDbType().getText());
       newSettingobject.setDbUrl(view.getChangeDbUrl().getText());
       newSettingobject.setDbUserName(view.getChangeUserName().getText());
       newSettingobject.setDbUserPwd(view.getChangePassword().getText());
       newSettingobject.setDriverClassName(view.getChangeDriverClassName().getText());
       return newSettingobject;
      }
      
      
      
    //  private DbSettingObject checkObjectContent(){
    //   
    //  }
      
     }
    }
    
    

    當我們的元系都設計好的時候,就可以建立起來
    ....
    ....
    DbSettingModel dbSettingmodel = new DbSettingModel();
    DbSettingView dbSettingPanel = new DbSettingView(dbSettingmodel);
    DbSettingController controller = new DbSettingController(dbSettingmodel,dbSettingPanel);        
    tabbedPane.addTab("DataBase Setting", icon, dbSettingPanel,"change db setting values");
    dbSettingPanel.setPreferredSize(new java.awt.Dimension(551, 211));
    ...
    ...
    



    Reference:

    Java SE Application Design With MVC
    Model-View-Controller (MVC) Structure
    A Swing Architecture Overview
    Building Graphical User Interfaces
    with the MVC Pattern

    Javaworld.com - MVC meets Swing
    Javaworld(TW) Swing MVC and Model

  • 2011年6月16日 星期四

    XSLT - Using XSLT to generate HTML from XML

    可擴展樣式錶轉換語言(Extensible Stylesheet Language Transformations,簡稱XSLT) , 前陣子剛碰到這玩意, 可以用來對XML檔做轉換的動作, 這次是用在把XML > HTML ,XSLT本身也是一份XML檔案,所以它也必須遵守嚴格的XML規範。 XSLT Wiki 有一些說明,還有一些link , 建議可以先看看XSLT的簡介, 還有XPATH的範例說明等,下面2個連結可以先參考看看:
    XPATH 語法
    XSLT 基礎

    這邊主要是針對XML to HTML做處理, 還有在XSLT裡面使用java method , javabean 來達到一些效果。

    原始的XML檔案大概長這樣,實作EDRM 的mail XML 檔會長的...非常難看
    <?xml version='1.0' encoding='UTF-8' ?>
      <Root caseId="Case1" description="Test Case" locale="US" majorVersion="1" minorVersion="2" rootFilePath="/temp/">
        <batch>
          <Document MimeType="multipart/mixed" DocType="Header" DocID="1">
            <FieldValues>
              <contentType>multipart/mixed;
     boundary="------------010709010402030905080005"</contentType>
              <mailAuthor>N2&lt;tamino2@tsai.james&gt;</mailAuthor>
              <mailCC>tamino0@tsai.james</mailCC>
              <mailSubject>2011/06/14-test01</mailSubject>
              <mailTo>tamino1@tsai.james,tamino0@tsai.james</mailTo>
              <messageID>&lt;4DF6BDD9.7090905@tsai.james&gt;</messageID>
            </FieldValues >
          </Document>
          <Document MimeType="multipart/alternative" DocType="MIME Entity" DocID="2">
            <FieldValues>
              <contentType>multipart/alternative;
     boundary="------------080407070307020309080702"</contentType>
            </FieldValues>
          </Document>
          <Document MimeType="text/plain" DocType="MIME Entity" DocID="3">
            <FieldValues>
              <content>aabbccd
    *eeffggy*
    myImg1
    
    
    myImg2
    
    </content>
              <contentType>text/plain; charset=UTF-8; format=flowed</contentType>
            </FieldValues>
          </Document>
          <Document MimeType="multipart/related" DocType="MIME Entity" DocID="4">
            <FieldValues>
              <contentType>multipart/related;
     boundary="------------000300090903000901010201"</contentType>
            </FieldValues>
          </Document>
          <Document MimeType="text/html" DocType="MIME Entity" DocID="5">
            <FieldValues>
              <content>&lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&gt;
    &lt;html&gt;
      &lt;head&gt;
    
        &lt;meta http-equiv="content-type" content="text/html; charset=UTF-8"&gt;
      &lt;/head&gt;
      &lt;body text="#000000" bgcolor="#ffffff"&gt;
        aabbccd&lt;br&gt;
        &lt;b&gt;&lt;big&gt;&lt;big&gt;&lt;big&gt;eeffggy&lt;/big&gt;&lt;/big&gt;&lt;/big&gt;&lt;/b&gt;&lt;br&gt;
        myImg1&lt;br&gt;
        &lt;img src="cid:part1.03050007.07010308@tsai.james" alt=""&gt;&lt;br&gt;
        &lt;br&gt;
        myImg2&lt;br&gt;
        &lt;img src="cid:part2.01030500.03060103@tsai.james" alt=""&gt;&lt;br&gt;
      &lt;/body&gt;
    &lt;/html&gt;
    </content>
              <contentType>text/html; charset=UTF-8</contentType>
            </FieldValues>
          </Document>
          <Document MimeType="image/jpeg" DocType="MIME Entity" DocID="6">
            <FieldValues>
              <content>/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a
    HBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy
    MjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAAaAVADASIA
    </content>
              <contentID>&lt;part1.03050007.07010308@tsai.james&gt;</contentID>
              <contentTransferEncoding>base64</contentTransferEncoding>
              <contentType>image/jpeg;
     name="new_pagingtoolbar.JPG"</contentType>
            </FieldValues>
          </Document>
          <Document MimeType="image/jpeg" DocType="MIME Entity" DocID="7">
            <FieldValues>
              <content>/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a
    HBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIy
    </content>
              <contentID>&lt;part2.01030500.03060103@tsai.james&gt;</contentID>
              <contentTransferEncoding>base64</contentTransferEncoding>
              <contentType>image/jpeg;
     name="SaveAs_SubMenu.JPG"</contentType>
            </FieldValues>
          </Document>
          <Document MimeType="application/vnd.openxmlformats-officedocument.wordprocessingml.document" DocType="Attach File" DocID="8">
            <FieldValues>
              <contentTransferEncoding>7bit</contentTransferEncoding>
              <contentType>application/vnd.openxmlformats-officedocument.wordprocessingml.document;
     name="temp.docx"</contentType>
            </FieldValues>
          </Document>
          <Document MimeType="application/octet-stream" DocType="Attach File" DocID="9">
            <FieldValues>
              <contentTransferEncoding>base64</contentTransferEncoding>
              <contentType>application/octet-stream;
     name="test.rar"</contentType>
            </FieldValues>
          </Document>
        </batch>
        <fields/>
      </Root>
    
    為了閱讀方便,我把file content 的base64 encode 部份刪掉部份,所以上面的xml是會有問題的, 一般使用XSLT在做XML轉換的時候,如果XML定義檔很單純簡單的話,XSL檔的設計上就會簡單很多,碰到上面這種XML很復雜的時候處理上就會麻煩點。先來看看轉出來的結果是長什麼樣子, 轉成HTML 檔 或著是回傳HTML String ,大概像這樣:

    說穿了只是把原始的E-Mail內容轉換成EDRM XML , 然後會有一堆XML檔,透過XSLT轉換成HTML View , 所以在XSL的設計上就要把相關的屬性取出來,然後變成HTML, 下面是實作出來的XSL檔
    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" 
     xmlns:cu="com.mypackage.util.XsltCommonUtils"
     xmlns:obj="com.mypackage.xsltObject.XsltDataObject"
     exclude-result-prefixes="cu obj" >
     
     <xsl:output method="html" encoding="UTF-8" />
     
     
     <xsl:variable name="Documents">
      <xsl:value-of select="/Documents" />
     </xsl:variable>
     <xsl:variable name="From"> 
      <xsl:value-of select="cu:processEscapingString(//FieldValues/mailAuthor)" disable-output-escaping="yes"/>
     </xsl:variable>
     <xsl:variable name="To">
      <xsl:value-of select="//FieldValues/mailTo" />
     </xsl:variable>
     <xsl:variable name="Cc">
      <xsl:value-of select="//FieldValues/mailCC" />
     </xsl:variable>
     <xsl:variable name="Bcc">
      <xsl:value-of select="//FieldValues/mailBCC" />
     </xsl:variable>
     <xsl:variable name="SendDate">
      <xsl:value-of select="//FieldValues/mailSentDate" />
     </xsl:variable>
     <xsl:variable name="Subject">
      <xsl:value-of select="//FieldValues/mailSubject" />
     </xsl:variable>
     <xsl:variable name="uid">
      <xsl:value-of select="//FieldValues/messageID" />
     </xsl:variable>
     
     <xsl:variable name="bodyObj" select="obj:new()"/>  
     
     <xsl:param name="htmlBody">
      <xsl:for-each select="//Document">
       <xsl:choose>
        <xsl:when test="contains(@MimeType,'text/html')">
            <xsl:value-of select="cu:setXlstDataObject($bodyObj,FieldValues/content)" disable-output-escaping="yes"/>     
            <xsl:value-of select="cu:setXlsDataObjectIsHtml($bodyObj)"/>
        </xsl:when>
       </xsl:choose>
      </xsl:for-each>       
     </xsl:param>
    
     <xsl:variable name="textBody">
      <xsl:if test="not(cu:getXlsDataObjectIsHtml($bodyObj))">
       <xsl:for-each select="//Document">
        <xsl:choose>
        <xsl:when test="contains(@MimeType,'text/plain')">
            <xsl:if test="contains(@DocType,'MIME Entity')">
             <xsl:value-of select="//FieldValues/content" />
            </xsl:if> 
        </xsl:when>
       </xsl:choose>
       </xsl:for-each>    
      </xsl:if> 
     </xsl:variable>
     
     <xsl:template match="/">
      <html>
       <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <style type="text/css">
         BODY, TD {
         font-family: Arial;
         color: black;
         font-size: 9pt;
         font-weight: normal;
         text-decoration: none; }
         * {margin:0; padding:0;}
         .header{
         background-image: url(img/bg_line.jpg);
         background-repeat: repeat-x;
         background-position: left top;
         height: 94px;
         width: 100%;
         }
         .headerbg{
         background-image: url(img/bg.jpg);
         background-repeat: no-repeat;
         background-position: left top;
         height: 94px;
         width: 100%;
         }
         .main{
         margin-right: 5px;
         padding-top: 15px;
         width: 200px;
         }
         .right{
         float: right;
         margin-right: 5px;
         margin-left: 3px;
         padding-top: 4px;
         text-align: right;
         }
         .left{
         float: left;
         margin-right: 5px;
         margin-left: 3px;
         padding-top: 4px;
         text-align: left;
         }
         .logoimg{
         width: 50px;
         padding-right: 5px;
         padding-left: 10px;
         padding-top: 15px;
         }
    
         .input_a{
         border: 1px solid #333;
         font-family: Arial;
         font-size: 9pt;
         font-weight: normal;
         color: #000;
         height: 18px;
         width: 312px;
         background-image: url(img/usericon.jpg);
         background-repeat: no-repeat;
         background-color: #FFF;
         padding-left: 17px;
         background-position: 2px;
         }
         .input_b{
         border: 1px solid #333;
         font-family: Arial;
         font-size: 9pt;
         font-weight: normal;
         color: #000;
         height: 18px;
         width: 500px;
         background-color: #FFF;
         padding-left: 0px;
         background-position: 2px;
         }
         TABLE.tablestyle1 TD, TABLE.tablestyle1
         TD { border: 1 solid black;
         }
         TABLE.tablestyle1 { border-collapse:
         collapse; border-spacing: 0px
         0px; empty-cells: show }
        </style>
       </head>
       <body>
       
        
        <table width="100%" border="0" cellspacing="0" cellpadding="0">
         <tr>
          <td align="left" valign="top" class="headerbg">
           <table width="100%" border="0" cellspacing="0" cellpadding="0">
            <tr>
             <td align="left" valign="top" class="logoimg">
              <img src="img/logo.gif" width="60" height="60" alt="" />
             </td>
             <td align="left" valign="top" class="main">
              <b>
               <xsl:value-of select="$From"
                disable-output-escaping="yes" />
              </b>
              <br />
              <xsl:value-of select="$SendDate" />
             </td>
             <td align="left" valign="top" class="left">
              <table width="50px" border="0" cellspacing="1"
               cellpadding="0">
               <tr>
                <td align="right" valign="top">To%uFF1A</td>
                <td width="20%" align="right" valign="top">
                 <xsl:text disable-output-escaping="yes"><![CDATA[<input type="text" size="50" class="input_b" readonly="true" value=']]></xsl:text>
                 <xsl:value-of select="$To"
                  disable-output-escaping="yes" />
                 <xsl:text disable-output-escaping="yes"><![CDATA['/>]]></xsl:text>
                </td>
               </tr>
               <tr>
                <td align="right" valign="top">cc%uFF1A</td>
                <td align="right" valign="top">
                 <xsl:text disable-output-escaping="yes"><![CDATA[<input type="text" size="50" class="input_b" readonly="true" value=']]></xsl:text>
                 <xsl:value-of select="$Cc"
                  disable-output-escaping="yes" />
                 <xsl:text disable-output-escaping="yes"><![CDATA['/>]]></xsl:text>
                </td>
               </tr>
               <tr>
                <td align="right" valign="top">bcc%uFF1A</td>
                <td align="right" valign="top">
                 <xsl:text disable-output-escaping="yes"><![CDATA[<input type="text" size="50" class="input_b" readonly="true" value=']]></xsl:text>
                 <xsl:value-of select="$Bcc"
                  disable-output-escaping="yes" />
                 <xsl:text disable-output-escaping="yes"><![CDATA['/>]]></xsl:text>
                </td>
               </tr>
               <tr>
                <td align="right" valign="top">Subject%uFF1A</td>
                <td align="right" valign="top">
                 <xsl:text disable-output-escaping="yes"><![CDATA[<input type="text" size="50" class="input_b" readonly="true" value=']]></xsl:text>
                 <xsl:value-of select="$Subject"
                  disable-output-escaping="yes" />
                 <xsl:text disable-output-escaping="yes"><![CDATA['/>]]></xsl:text>
                </td>
               </tr>
              </table>
             </td>
            </tr>
           </table>
          </td>
         </tr>
        </table>
        <br />
        <table width="100%">
         <tr>
          <td>
           
           <!-- 
           <xsl:for-each select="//item[@name='Body']/itemdata[@type='19']">
            <pd4ml-include encoding="qp" debug="false">
             <xsl:copy-of select="text()" />
            </pd4ml-include>
           </xsl:for-each>
    
           <xsl:for-each select="//item/rawitemdata[@type='19']">
            <table border="0" width="100%">
             <tr>
              <td style="word-wrap: break-word">
               <pd4ml-include xencoding="qp" debug="false">
                <xsl:copy-of select="text()" />
               </pd4ml-include>
              </td>
             </tr>
            </table>
           </xsl:for-each>
            -->
           <!-- 
           <xsl:for-each
            select="//item[@name='$FILE']/object/file[@name='mime.htm']/filedata">
            <pd4ml-include debug="false">
             <xsl:attribute name="encoding"><xsl:value-of
              select="../@encoding" /></xsl:attribute>
             <xsl:copy-of select="text()" />
            </pd4ml-include>
           </xsl:for-each>
           
           <xsl:apply-templates select="item" />
            -->
           <!-- commented out because file[contains(@name,'mime.gif')] causes 
            java.lang.ArrayIndexOutOfBoundsException in Xalan under some conditions xsl:for-each 
            select="//item[@name='$FILE']/object/file[contains(@name,'mime.gif')]/filedata"> 
            <img> <xsl:attribute name="src">data:image/gif;base64,<xsl:value-of select="normalize-space(.)" 
            disable-output-escaping="yes"/></xsl:attribute> <xsl:attribute name="border">0</xsl:attribute> 
            </img> </xsl:for-each -->
          </td>
         </tr>
         </table>
         <!-- 
        <xsl:for-each select="//Document">
         <xsl:choose>
          <xsl:when test="contains(@DocType,'MIME Entity')">
           <xsl:value-of select="Document/FieldValues/ContentID" disable-output-escaping="yes"/>  
           <xsl:value-of select="cu:testNodeSet(Document/FieldValues)" />
          </xsl:when>
       
         </xsl:choose>
        </xsl:for-each>
         -->
         
         
         <!--  to get body string with inline images. -->
         <xsl:apply-templates select="//Document" ></xsl:apply-templates>
         
         <xsl:value-of select="$textBody" disable-output-escaping="yes"/>
         <xsl:value-of select="cu:getXlstDataObject($bodyObj)" disable-output-escaping="yes"/>
         
       </body>
      </html>
     </xsl:template>
     
     
     <xsl:template match="Document">    
       <xsl:choose>
         <xsl:when test="contains(@MimeType,'image')">
           <xsl:variable name="contentId">
            <xsl:value-of select="FieldValues/contentID" disable-output-escaping="yes"/> 
           </xsl:variable>
           <xsl:variable name="content">       
            <xsl:value-of select="FieldValues/content" disable-output-escaping="yes"/>
           </xsl:variable>
           <xsl:variable name="fileType">       
            <xsl:value-of select="Files/File/@FileType" disable-output-escaping="yes"/>
           </xsl:variable>
    
           <xsl:variable name="filePath">
            <xsl:value-of select="Files/File/ExternalFile/@FilePath" disable-output-escaping="yes"/>
           </xsl:variable>
           <xsl:variable name="fileName">       
            <xsl:value-of select="Files/File/ExternalFile/@FileName" disable-output-escaping="yes"/>
           </xsl:variable>
           <xsl:variable name="hashMd5">
            <xsl:value-of select="Files/File/ExternalFile/@Hash" disable-output-escaping="yes"/>
           </xsl:variable>
          <xsl:value-of select="cu:setDataObjectBodyByInlineImageUrl($bodyObj,$contentId,$fileType,$content,$fileName,$hashMd5) "  disable-output-escaping="yes"/>               
        </xsl:when>
        <xsl:when test="contains(@DocType,'Attach File')">
         <!-- do something -->
        </xsl:when>
       </xsl:choose>   
     </xsl:template>
     
    </xsl:stylesheet> 
    


    還有相關的Java Class
    XsltDataObject.java
    package com.mypackage.xsltObject;
    
    public class XsltDataObject {
     private  String bodyString;
     private boolean isHtml = false;
     
     
     
     
     public boolean isHtml() {
      return isHtml;
     }
    
     public void setHtml(boolean isHtml) {
      this.isHtml = isHtml;
     }
    
     public  String getBodyString() {
      return bodyString;
     }
    
     public  void setBodyString(String bodyString) {
      this.bodyString = bodyString;
     }
    }
    

    還有XsltCommonUtils.java
    package com.mypackage.util;
    
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileReader;
    import java.io.IOException;
    
    import com.mypackage.xsltObject.XsltDataObject;
    
    public class XsltCommonUtils {
     
     
     
     public static void setXlstDataObject(XsltDataObject object,String bodyString){
      String bodyStr = bodyString;
      bodyStr = bodyStr.replaceAll("cid:", "");
      object.setBodyString(bodyStr);
     }
     
     public static String getXlstDataObject(XsltDataObject object){
      return object.getBodyString();
     }
     
     public static void setXlsDataObjectIsHtml(XsltDataObject object){
      System.out.println("setXlsDataObjectIsHtml to true ");
      object.setHtml(true);
     }
     
     public static boolean getXlsDataObjectIsHtml(XsltDataObject object){
      System.out.println("getXlsDataObjectIsHtml  = "  object.isHtml());
      return object.isHtml();
     }
     
     
     public static void setDataObjectBodyByInlineImageUrl(XsltDataObject inObject,
       String contentId, String fileType, String content,
       String fileName, String hashMd5) {
      
      
      //for debug
      System.out.println("contentId = " contentId);
      System.out.println("fileType = " fileType);
    
      System.out.println("fileName = " fileName);
      
      
      String contentIdStr = replaceContentId(contentId.trim());
      System.out.println("contentIdStr =" contentIdStr);
      StringBuffer resultString = new StringBuffer();
      String bodyString = inObject.getBodyString();
      
      resultString.append("data:");
      resultString.append(fileType);
      resultString.append(";base64,");
      
      
      //will be change to get file name rule.
      /*
      String fullFileName = filePath "/"   contentIdStr;
      System.out.println("fullFileName = " fullFileName);
      fullFileName =  fullFileName.replaceAll("\\\\", "/");
      
      File f = new File(fullFileName);
      BufferedReader reader = null;
      StringBuffer contents = new StringBuffer();
      
      if(f.exists()){
       try {
        reader = new BufferedReader(new FileReader(f));
        String text = null;
        while ((text = reader.readLine()) != null) {
         contents.append(text);
        }
        reader.close();
       } catch (FileNotFoundException e) {    
        e.printStackTrace();
       } catch (IOException e) {
        e.printStackTrace();
       }
      }
      */
    //  System.out.println("contents = "  contents.toString());
      resultString.append(content);
      
      bodyString = bodyString.replaceAll(contentIdStr, resultString.toString());
      
    //  System.out.println("Result bodyString = "   bodyString);  
      inObject.setBodyString(bodyString);
     }
     
     
     private static String replaceContentId(String contentId){
      String result = contentId;
      result = result.replaceAll("&lt;", "");
      result = result.replaceAll("&gt;", "");
      result = result.replaceAll("<", "");
      result = result.replaceAll(">", "");
      
      return result;
     }
     
     public static String processEscapingString(String inputString){
      System.out.println("processEscapingString = " inputString);
      String result = inputString;
      result = result.replaceAll("&lt;", "&lt; ");
      result = result.replaceAll("&gt;", " &gt;");
      result = result.replaceAll("<", "< ");
      result = result.replaceAll(">", " >");
      System.out.println("processEscapingString result= " result);
      return result;
      
     }
    
    }
    

    可以先別理會Java Class在做什麼事, 在原始的XSL 檔裡面可以注意下面這個
    <xsl:variable name="bodyObj" select="obj:new()"/> 
    
    這邊會設定一個variable 為bodyObj ,XSLT 的variable 一旦設定後就不可變更,但是我們可以對這個Java Object的值做處理。

    看看下面的param設定
    <xsl:param name="htmlBody">
      <xsl:for-each select="//Document">
       <xsl:choose>
        <xsl:when test="contains(@MimeType,'text/html')">
            <xsl:value-of select="cu:setXlstDataObject($bodyObj,FieldValues/content)" disable-output-escaping="yes"/>     
            <xsl:value-of select="cu:setXlsDataObjectIsHtml($bodyObj)"/>
        </xsl:when>
       </xsl:choose>
      </xsl:for-each>       
     </xsl:param>
    

    cu:setXlstDataObject , cu是我們指定的xmlns name = cu , 指向java class = com.mypackage.util.XsltCommonUtils, 所以當你在XSLT裡調用
    cu:setXlstDataObject($bodyObj,FieldValues/content)
    
    會呼叫XsltCommonUtils的setXlstDataObject method,
    public static void setXlstDataObject(XsltDataObject object,String bodyString){
      String bodyStr = bodyString;
      bodyStr = bodyStr.replaceAll("cid:", "");
      object.setBodyString(bodyStr);
    }
    
    注意在XSL 裡面$bodyObj會自動對應Java method裡面的setXlstDataObject為XsltDataObject, 然後在XsltCommonUtils就寫一些針對JavaBean做處理的動作把XSL裡面的$bodyObj再做操作處理。
    相同的處理在XSL 跟 Java之間的物件型態轉換可以自己參考文件。

    所以我們的XSL檔跟XML都有了之後就是要透過TransformerFactory轉換成HTML

    XmlToXLSTtest.java
    package com;
    
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    
    import javax.xml.transform.Transformer;
    import javax.xml.transform.TransformerConfigurationException;
    import javax.xml.transform.TransformerException;
    import javax.xml.transform.TransformerFactory;
    import javax.xml.transform.stream.StreamResult;
    import javax.xml.transform.stream.StreamSource;
    
    public class XmlToXLSTtest {
     
     /**       
      * @param args 
      */
     public static void main(String[] args) {
      TransformerFactory tFactory = TransformerFactory.newInstance();
      Transformer transformer; 
      try {  
       transformer = tFactory.newTransformer(new javax.xml.transform.stream.StreamSource("test.xsl"));
       
    //   StreamSource xslt = new StreamSource(dxlReader);
    //   StreamResult result = new StreamResult(outWriter);
       
       transformer.transform(new javax.xml.transform.stream.StreamSource("EDRM_Sample-2.xml"), 
                       new javax.xml.transform.stream.StreamResult(new FileOutputStream("EDRM.html")));
       
       System.out.println("transformer finished.");
      } catch (TransformerConfigurationException e) {
       e.printStackTrace();
      } catch (FileNotFoundException e) {
       e.printStackTrace();
      } catch (TransformerException e) {
       e.printStackTrace();
      }
     }
    
    }
    
    


    Reference:
    Java and XSLT
    XSLT Wiki
    Processing XML with Java
    XSL Transformations (XSLT) Version 2.0 (W3c)

    XPATH 語法
    XSLT 基礎
    简单的 Xalan 扩展函数