<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    tory320

      BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
      10 隨筆 :: 0 文章 :: 1 評論 :: 0 Trackbacks

    2008年3月7日 #

    Design Principle
    Identify the aspects of your application that vary and separate them from what stays the same.
    Here's another way to think about this principle: take the parts that vary and encapsulate them, so that later you can alter or extends the parts that vary without affecting those that don't.
    As simple as this concept is, it forms the basis for almost every design pattern. All patterns provide a way to let some part of a system vary independently of all other parts.

    Each set of class will hold all the implementations of their respective behavior. For instance, we might have one clss that implements quarking, another implements squaking, and another that implements silence.

    To separate thest behaviors from the Duck class, we'll pull both methods out of the duck class and create a new set of class to represent each behavior.

    This is in contrast to the way we were doing things before, where a behavior either came from a concrete implementation in the suprerclass Duck, or by providing a specialized implementation in the sub class itself. In both cases we were relying on an implementation. We were locked into using that specific implemetation and there was no room for changing out the behavior.

    And the same is true for the duck's flying behavior.

    Okay, now that we've done the deep dive on the duck simulator design, it's time to come back up for air and take a look at the big picture.

    Below is the entire reworked class structure. We have everything you'd expect: ducks extending Duck. fly behavior implementing FlyBehavior and quack behavior implementing QuackBehavior.

    Notice also that we've started to describe things a little differntly. Instead of thinking of the duck behaviors as a set of behaviors, we'll start thinking of them ad a family of algorithms. Think about it: in the SimUDuck design, the algorithms represent things a duck would do , but we could just as easily use the same techniques for a set of classes that implement the ways to compute state sales tax by different states.

    posted @ 2008-03-07 17:58 tory 閱讀(136) | 評論 (0)編輯 收藏

    2007年1月30日 #

    /**
    ?* //FileOperate.java
    ?* 文件的各種操作
    ?* 楊彩 http://blog.sina.com.cn/m/yangcai
    ?* 文件操作 1.0
    ?*/
    ?
    //package common;
    ?
    import java.io.*;
    ?
    public class FileOperate
    {
    ?static boolean exitnow=false;
    ?static String aa,bb;
    ? public FileOperate() {
    ? }
    ?
    ? /**
    ?? * 新建目錄
    ?? */
    ? public void newFolder(String folderPath) {
    ??? try
    ??? {
    ????? String filePath = folderPath;
    ????? filePath = filePath.toString();
    ????? File myFilePath = new File(filePath);
    ????? if(!myFilePath.exists())
    ????? {
    ??????? myFilePath.mkdir();
    ????? }
    ????? System.out.println("新建目錄操作 成功執行");
    ??? }
    ??? catch(Exception e)
    ??? {
    ????? System.out.println("新建目錄操作出錯");
    ????? e.printStackTrace();
    ??? }
    ? }
    ?
    ? /**
    ?? * 新建文件
    ?? */
    ? public void newFile(String filePathAndName, String fileContent)
    ? {
    ?
    ??? try
    ??? {
    ????? String filePath = filePathAndName;
    ????? filePath = filePath.toString();
    ????? File myFilePath = new File(filePath);
    ????? if (!myFilePath.exists())
    ????? {
    ??????? myFilePath.createNewFile();
    ????? }
    ????? FileWriter resultFile = new FileWriter(myFilePath);
    ????? PrintWriter myFile = new PrintWriter(resultFile);
    ????? String strContent = fileContent;
    ????? myFile.println(strContent);
    ????? resultFile.close();
    ????? System.out.println("新建文件操作 成功執行");
    ??? }
    ??? catch (Exception e) {
    ????? System.out.println("新建目錄操作出錯");
    ????? e.printStackTrace();
    ?
    ??? }
    ?
    ? }
    ?
    ? /**
    ?? * 刪除文件
    ?? */
    ? public void delFile(String filePathAndName) {
    ??? try {
    ????? String filePath = filePathAndName;
    ????? filePath = filePath.toString();
    ????? File myDelFile = new File(filePath);
    ????? myDelFile.delete();
    ????? System.out.println("刪除文件操作 成功執行");
    ??? }
    ??? catch (Exception e) {
    ????? System.out.println("刪除文件操作出錯");
    ????? e.printStackTrace();
    ?
    ??? }
    ?
    ? }
    ?
    ? /**
    ?? * 刪除文件夾
    ?? */
    ? public void delFolder(String folderPath)
    ? {
    ??? try
    ??? {
    ????? delAllFile(folderPath); //刪除完里面所有內容
    ????? String filePath = folderPath;
    ????? filePath = filePath.toString();
    ????? File myFilePath = new File(filePath);
    ????? myFilePath.delete(); //刪除空文件夾
    ????? System.out.println("刪除文件夾操作 成功執行");
    ??? }
    ??? catch (Exception e)
    ??? {
    ????? System.out.println("刪除文件夾操作出錯");
    ????? e.printStackTrace();
    ?
    ??? }
    ?
    ? }
    ?
    ? /**
    ?? * 刪除文件夾里面的所有文件
    ?? * @param path String 文件夾路徑 如 c:/fqf
    ?? */
    ? public void delAllFile(String path)
    ? {
    ??? File file = new File(path);
    ??? if(!file.exists())
    ??? {
    ????? return;
    ??? }
    ??? if(!file.isDirectory())
    ??? {
    ????? return;
    ??? }
    ??? String[] tempList = file.list();
    ??? File temp = null;
    ??? for (int i = 0; i < tempList.length; i++)
    ??? {
    ????? if(path.endsWith(File.separator))
    ????? {
    ??????? temp = new File(path + tempList[i]);
    ????? }
    ????? else
    ????? {
    ??????? temp = new File(path + File.separator + tempList[i]);
    ????? }
    ????? if (temp.isFile())
    ????? {
    ??????? temp.delete();
    ????? }
    ????? if (temp.isDirectory())
    ????? {
    ??????? delAllFile(path+"/"+ tempList[i]);//先刪除文件夾里面的文件
    ??????? delFolder(path+"/"+ tempList[i]);//再刪除空文件夾
    ????? }
    ??? }
    ????????? System.out.println("刪除文件操作 成功執行");?
    ? }
    ?
    ? /**
    ?? * 復制單個文件
    ?? * @param oldPath String 原文件路徑 如:c:/fqf.txt
    ?? * @param newPath String 復制后路徑 如:f:/fqf.txt
    ?? */
    ? public void copyFile(String oldPath, String newPath) {
    ??? try {
    ????? int bytesum = 0;
    ????? int byteread = 0;
    ????? File oldfile = new File(oldPath);
    ????? if (oldfile.exists())
    ????? { //文件存在時
    ??????? InputStream inStream = new FileInputStream(oldPath); //讀入原文件
    ??????? FileOutputStream fs = new FileOutputStream(newPath);
    ??????? byte[] buffer = new byte[1444];
    ??????? int length;
    ??????? while ( (byteread = inStream.read(buffer)) != -1) {
    ????????? bytesum += byteread; //字節數 文件大小
    ????????? System.out.println(bytesum);
    ????????? fs.write(buffer, 0, byteread);
    ??????? }
    ??????? inStream.close();
    ????? }
    ??????????? System.out.println("刪除文件夾操作 成功執行");?
    ??? }
    ??? catch (Exception e) {
    ????? System.out.println("復制單個文件操作出錯");
    ????? e.printStackTrace();
    ?
    ??? }
    ?
    ? }
    ?
    ? /**
    ?? * 復制整個文件夾內容
    ?? * @param oldPath String 原文件路徑 如:c:/fqf
    ?? * @param newPath String 復制后路徑 如:f:/fqf/ff
    ?? */
    ? public void copyFolder(String oldPath, String newPath) {
    ?
    ??? try
    ??? {
    ????? (new File(newPath)).mkdirs(); //如果文件夾不存在 則建立新文件夾
    ????? File a=new File(oldPath);
    ????? String[] file=a.list();
    ????? File temp=null;
    ????? for (int i = 0; i < file.length; i++)
    ????? {
    ??????? if(oldPath.endsWith(File.separator))
    ??????? {
    ????????? temp=new File(oldPath+file[i]);
    ??????? }
    ??????? else{
    ????????? temp=new File(oldPath+File.separator+file[i]);
    ??????? }
    ?
    ??????? if(temp.isFile())
    ??????? {
    ????????? FileInputStream input = new FileInputStream(temp);
    ????????? FileOutputStream output = new FileOutputStream(newPath + "/" +
    ????????????? (temp.getName()).toString());
    ????????? byte[] b = new byte[1024 * 5];
    ????????? int len;
    ????????? while ( (len = input.read(b)) != -1)
    ????????? {
    ??????????? output.write(b, 0, len);
    ????????? }
    ????????? output.flush();
    ????????? output.close();
    ????????? input.close();
    ??????? }
    ??????? if(temp.isDirectory())
    ??????? {//如果是子文件夾
    ????????? copyFolder(oldPath+"/"+file[i],newPath+"/"+file[i]);
    ??????? }
    ????? }
    ??????????? System.out.println("復制文件夾操作 成功執行");?
    ??? }
    ??? catch (Exception e) {
    ????? System.out.println("復制整個文件夾內容操作出錯");
    ????? e.printStackTrace();
    ?
    ??? }
    ?
    ? }
    ?
    ? /**
    ?? * 移動文件到指定目錄
    ?? * @param oldPath String 如:c:/fqf.txt
    ?? * @param newPath String 如:d:/fqf.txt
    ?? */
    ? public void moveFile(String oldPath, String newPath) {
    ??? copyFile(oldPath, newPath);
    ??? delFile(oldPath);
    ?
    ? }
    ?
    ? /**
    ?? * 移動文件到指定目錄
    ?? * @param oldPath String 如:c:/fqf.txt
    ?? * @param newPath String 如:d:/fqf.txt
    ?? */
    ? public void moveFolder(String oldPath, String newPath) {
    ??? copyFolder(oldPath, newPath);
    ??? delFolder(oldPath);
    ?
    ? }
    ?
    ? public static void main(String args[])
    ? {
    ? ?System.out.println("使用此功能請按[1]? 功能一:新建目錄");
    ? ?System.out.println("使用此功能請按[2]? 功能二:新建文件");
    ? ?System.out.println("使用此功能請按[3]? 功能三:刪除文件");
    ? ?System.out.println("使用此功能請按[4]? 功能四:刪除文件夾");
    ? ?System.out.println("使用此功能請按[5]? 功能五:刪除文件夾里面的所有文件");
    ? ?System.out.println("使用此功能請按[6]? 功能六:復制文件");
    ? ?System.out.println("使用此功能請按[7]? 功能七:復制文件夾的所有內容");
    ? ?System.out.println("使用此功能請按[8]? 功能八:移動文件到指定目錄");
    ? ?System.out.println("使用此功能請按[9]? 功能九:移動文件夾到指定目錄");
    ? ?System.out.println("使用此功能請按[10] 退出程序");
    ? ?
    ?while(!exitnow)
    ?{
    ? ??FileOperate fo=new FileOperate();
    ? ??try
    ? ??{
    ? ??BufferedReader Bin=new BufferedReader(new InputStreamReader(System.in));
    ? ??String a=Bin.readLine();
    ? ??int b=Integer.parseInt(a);
    ? ??
    ? ??switch(b)
    ? ??{
    ? ???case 1:System.out.println("你選擇了功能一? 請輸入目錄名");??
    ? ????? aa=Bin.readLine();
    ? ????? fo.newFolder(aa);
    ? ????? break;
    ? ???case 2:System.out.println("你選擇了功能二? 請輸入文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? System.out.println("請輸入在"+aa+"中的內容");
    ? ????? bb=Bin.readLine();
    ? ????? fo.newFile(aa,bb);
    ? ????? break;
    ? ???case 3:System.out.println("你選擇了功能三? 請輸入文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? fo.delFile(aa);
    ? ????? break;
    ? ???case 4:System.out.println("你選擇了功能四? 請輸入文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? fo.delFolder(aa);
    ? ????? break;
    ? ???case 5:System.out.println("你選擇了功能五? 請輸入文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? fo.delAllFile(aa);
    ? ????? break;??
    ? ???case 6:System.out.println("你選擇了功能六? 請輸入文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? System.out.println("請輸入目標文件名");?
    ? ????? bb=Bin.readLine();
    ? ????? fo.copyFile(aa,bb);
    ? ????? break;
    ? ???case 7:System.out.println("你選擇了功能七? 請輸入源文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? System.out.println("請輸入目標文件名");?
    ? ????? bb=Bin.readLine();
    ? ????? fo.copyFolder(aa,bb);
    ? ????? break;? ?????
    ? ???case 8:System.out.println("你選擇了功能八? 請輸入源文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? System.out.println("請輸入目標文件名");?
    ? ????? bb=Bin.readLine();
    ? ????? fo.moveFile(aa,bb);
    ? ????? break;
    ? ??? ?case 9:System.out.println("你選擇了功能九? 請輸入源文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? System.out.println("請輸入目標文件名");?
    ? ????? bb=Bin.readLine();
    ? ????? fo.moveFolder(aa,bb);
    ? ????? break;? ?????
    ? ???case 10:exitnow=true;
    ? ?????? System.out.println("程序結束,請退出");
    ? ????? break;
    ? ???default:System.out.println("輸入錯誤.請輸入1-10之間的數");?? ???? ????? ?
    ? ?? }
    ? ??
    ? ??
    ? ??System.out.println("請重新選擇功能");
    ? ??
    ? ??
    ? ??}
    ? ??catch(Exception e)
    ? ??{
    ? ??System.out.println("輸入錯誤字符或程序出錯");
    ? ??}
    ? ??
    ?}? ?
    ?}
    }
    posted @ 2007-01-30 13:03 tory 閱讀(184) | 評論 (0)編輯 收藏

    2007年1月19日 #

    本文是在參閱了http://ivanl.javaeye.com/blog/24739基礎上完成的
    在看JPetStore的代碼時,發現它的分頁處理主要是通過返回PaginatedList對象來完成的。如:在CatalogService類中
    public?PaginatedList?getProductListByCategory(String?categoryId)?{?
    ????
    return?productDao.getProductListByCategory(categoryId);?
    ??}
    ?

    分頁是操作數據庫型系統常遇到的問題。分頁實現方法很多,但效率的差異就很大了。iBatis是通過什么方式來實現這個分頁的了。查看它的實現部分:
    ?
    返回的PaginatedList實際上是個接口,實現這個接口的是PaginatedDataList類的對象,查看PaginatedDataList類發現,每次翻頁的時候最后都會調用下面這段函數
    private?List?getList(int?idx,?int?localPageSize)?throws?SQLException?{?
    ????
    return?sqlMapExecutor.queryForList(statementName,?parameterObject,?(idx)?*?pageSize,?localPageSize);?
    ??}
    ?
    由于
    public?interface?SqlMapClient?extends?SqlMapExecutor,?SqlMapTransactionManager?{……}?

    所以實際的調用次序如下:
    SqlMapClientImpl.queryForPaginatedList->SqlMapSessionImpl.queryForPaginatedList?
    ->SqlMapExecutorDelegate.queryForPaginatedList->GeneralStatement.executeQueryForList?
    ->GeneralStatment.executeQueryWithCallback->GeneralStatment.executeQueryWithCallback?
    ->SqlExecutor.executeQuery->SqlExecutor.handleMultipleResults()->SqlExecutor.executeQuery->?handleResults?
    分頁處理的函數如下
    private?void?handleResults(RequestScope?request,?ResultSet?rs,?int?skipResults,?int?maxResults,?RowHandlerCallback?callback)?throws?SQLException?{?
    ????
    try?{?
    ??????request.setResultSet(rs);?
    ??????ResultMap?resultMap?
    =?request.getResultMap();?
    ??????
    if?(resultMap?!=?null)?{?
    ????????
    //?Skip?Results?
    ????????if?(rs.getType()?!=?ResultSet.TYPE_FORWARD_ONLY)?{?
    ??????????
    if?(skipResults?>?0)?{?
    ????????????rs.absolute(skipResults);?
    ??????????}
    ?
    ????????}
    ?else?{?
    ??????????
    for?(int?i?=?0;?i?<?skipResults;?i++)?{?
    ????????????
    if?(!rs.next())?{?
    ??????????????
    return;?
    ????????????}
    ?
    ??????????}
    ?
    ????????}
    ?
    ??
    ????????
    //?Get?Results?
    ????????int?resultsFetched?=?0;?
    ????????
    while?((maxResults?==?SqlExecutor.NO_MAXIMUM_RESULTS?||?resultsFetched?<?maxResults)?&&?rs.next())?{?
    ??????????Object[]?columnValues?
    =?resultMap.resolveSubMap(request,?rs).getResults(request,?rs);?
    ??????????callback.handleResultObject(request,?columnValues,?rs);?
    ??????????resultsFetched
    ++;?
    ????????}
    ?
    ??????}
    ?
    ????}
    ?finally?{?
    ??????request.setResultSet(
    null);?
    ????}
    ?
    ??}
    ?

    由此可見,iBatis的分頁主要依賴于jdbcdriver的如何實現以及是否支持rs.absolute(skipResults)。它并不是一個好的分頁方式。它先要取出所有的符合條件的記錄存入ResultSet對象,然后用absolute方法進行定位,來實現分頁。當記錄數較大(比如十萬條)時,整體的查詢速度將會變得很慢。
    所以分頁還是要考慮采用直接操作sql語句來完成。當然小批量的可以采用iBatis的分頁模式。一般分頁的sql語句與數據庫的具體實現有關
    mysql:?
    select?*?from?A?limit?startRow,endRow?
    oracle:?
    select?b.*?from?(select?a.*,rownum?as?linenum?from?(select?*?from?A)?a?where?rownum?<=?endRow)?b?where?linenum?>=?startRow?

    Hibernate的Oracle分頁采用的就是是拼湊RowNum的Sql語句來完成的。參考代碼如下:?
    ?
    ????????public?String?createOraclePagingSql(String?sql,?int?pageIndex,?int?pageSize){?
    ????????????
    int?m?=?pageIndex?*?pageSize;?
    ????????????
    int?n?=?m?+?pageSize;?
    ????????????
    return?"select?*?from?(?select?row_.*,?rownum?rownum_?from?(?"?+?sql?
    ????????????????????
    +?"?)?row_?where?rownum?<=?"?+?n??
    ????????????????????
    +?")?where?rownum_?>?"?+?m;?
    ????????}
    ?
    綜上,小批量(<2w)可以采用ibatis自帶的分頁類,大批量的還是直接操縱sql,當然也可以將這些sql自己進行封裝,或在包中封裝都可以。包封裝的示例代碼如下:
    一個封裝了分頁功能的Oracle Package
    create?or?replace?package?body?FMW_FY_HELPER?is
    PROCEDURE?GET_DATA(pi_sql?in?varchar,pi_whichpage?in?integer,pi_rownum?in?integer,
    po_cur_data?out?cur_DATA,po_allrownum?out?
    integer,pio_succeed?in?out?integer)
    as?
    v_cur_data?cur_DATA;
    v_cur_temp?cur_TEMP;
    v_temp?
    integer;
    v_sql?
    varchar(5000);
    v_temp1?
    integer;
    v_temp2?
    integer;
    begin
    pio_succeed?:
    =?1;
    v_sql?:
    =?'select?count(''a'')?from?(?'?||?pi_sql?||?')';
    execute?immediate?v_sql?into?v_temp;

    po_allrownum:
    =ceil(v_temp/pi_rownum);

    v_sql?:
    =?'';
    v_temp?:
    =pi_whichpage*pi_rownum?+?1;
    v_temp1:
    =(pi_whichpage-1)*pi_rownum?+?1;
    v_temp2:
    =pi_whichpage*pi_rownum;
    v_sql:
    =?'select?*?from?(select?rownum?as?rn,t.*?from?('?||?pi_sql?||')?t?where?rownum<'?||?to_char(v_temp)?||?')??where?rn?between?'?||?to_char(v_temp1)?||?'?and?'?||?to_char(v_temp2);
    open?v_cur_data?for?v_sql;
    if?v_cur_data?%notfound
    then
    pio_succeed:
    =-1;
    return;
    end?if;
    po_cur_DATA?:
    =?v_cur_data;
    end;
    posted @ 2007-01-19 13:02 tory 閱讀(828) | 評論 (0)編輯 收藏

    2006年12月29日 #

    使用JFreeChart生成熱點圖表
    2006-12-14 11:54
    <一>前言:

      JFreeChart是開放源代碼站點SourceForge.net上的一個JAVA項目。它的功能十分強大,能創建餅圖、柱狀圖(普通柱狀圖以及堆棧柱狀圖)、線圖、區域圖、分布圖、混合圖、甘特圖以及一些儀表盤等等,并可生成PNG或JPG圖片格式文件。
      本人在學習過程中發現,網上很多文章都是講一些JFreeChart的基本應用,而對JFreeChart生成熱點圖表這樣常用的功能雖有所提及卻沒有一個完整的例子,所以我就寫一個簡單示例供大家參考,希望對大家的學習有所幫助。?

      <二>示例說明:

      假設有一個關于程序員北京,上海,廣洲三地程序員學歷,開發語言,薪金情況的調查。首先要以餅圖顯示程序員學歷的分布情況(index.jsp)。點擊餅圖的每一部分會以柱狀圖顯示該層次程序員所用開發語言和薪金的情況(barview.jsp)。重點演示怎樣在餅圖上添加鏈接。?

      <三>準備工作:

      1.下載最新版本的JFreeChart,當前為jfreechart-1.0.0-rc1
    下載地址:http://www.jfree.org/jfreechart/index.html

      2.解壓文件,將jfreechart-1.0.0-rc1/lib下的jcommon-1.0.0-rc1.jar,jfreechart-1.0.0-rc1.jar復制到WEB應用的lib目錄下。

      3.在web.xml文件中增加以下內容:

    <servlet>?
    <servlet-name>DisplayChart</servlet-name>?
    <servlet-class>org.jfree.chart.servlet.DisplayChart</servlet-class>?
    </servlet>?
    <servlet-mapping>?
    <servlet-name>DisplayChart</servlet-name>?
    <url-pattern>/servletDisplayChart</url-pattern>?
    </servlet-mapping>?

      <四>餅圖頁面代碼(index.jsp)?


    <%@?page?contentType="text/html;charset=GBK"%>?
    <%@?page?import="org.jfree.data.general.DefaultPieDataset"%>?
    <%@?page?import="org.jfree.chart.*"%>?
    <%@?page?import="org.jfree.chart.plot.*"%>?
    <%@?page?import="org.jfree.chart.servlet.ServletUtilities"%>?
    <%@?page?import="org.jfree.chart.labels.StandardPieItemLabelGenerator"%>?
    <%@?page?import="org.jfree.chart.urls.StandardPieURLGenerator"%>?
    <%@?page?import="org.jfree.chart.entity.StandardEntityCollection"%>?
    <%@?page?import="java.io.*"%>?
    <HTML>?
    <HEAD>?
    <META?http-equiv=Content-Type?content="text/html;?charset=GBK">?
    <TITLE>nacl_zhuang@hotmail.com</TITLE>?
    </HEAD>?
    <BODY>?
    <%?

    DefaultPieDataset?data?=?new?DefaultPieDataset();?
    data.setValue("高中以下",370);?
    data.setValue("高中",1530);?
    data.setValue("大專",5700);?
    data.setValue("本科",8280);?
    data.setValue("碩士",4420);?
    data.setValue("博士",80);?

    PiePlot3D?plot?=?new?PiePlot3D(data);//3D餅圖?
    plot.setURLGenerator(new?StandardPieURLGenerator("barview.jsp"));//設定鏈接?
    JFreeChart?chart?=?new?JFreeChart("",JFreeChart.DEFAULT_TITLE_FONT,?plot,?true);?
    chart.setBackgroundPaint(java.awt.Color.white);//可選,設置圖片背景色?
    chart.setTitle("程序員學歷情況調查表");//可選,設置圖片標題?
    plot.setToolTipGenerator(new?StandardPieItemLabelGenerator());?
    StandardEntityCollection?sec?=?new?StandardEntityCollection();?
    ChartRenderingInfo?info?=?new?ChartRenderingInfo(sec);?
    PrintWriter?w?=?new?PrintWriter(out);//輸出MAP信息?
    //500是圖片長度,300是圖片高度?
    String?filename?=?ServletUtilities.saveChartAsPNG(chart,?500,?300,?info,?session);?
    ChartUtilities.writeImageMap(w,?"map0",?info,?false);?

    String?graphURL?=?request.getContextPath()?+?"/servlet/DisplayChart?filename="?+?filename;?

    %>?

    <P?ALIGN="CENTER">?
    <img?src="<%=?graphURL?%>"?width=500?height=300?border=0?usemap="#map0">?
    </P>?
    </BODY>?
    </HTML>?

      生成的圖片如下


      在瀏覽器中點右鍵->查看源文件會發現有以下一段HTML代碼:?

    <map?id="map0"?name="map0">?
    <area?shape="poly"?coords="247,61,250,61,250,123,250,123"?title="博士?=?80"?alt=""?href="barview.jsp?category=博士&pieIndex=0"/>?
    <area?shape="poly"?coords="148,112,153,102,160,92,170,83,182,76,196,70,212,65,229,62,247,61,250,123,250,123"?title="碩士?=?4,420"?alt=""?href="barview.jsp?category=碩士&pieIndex=0"/>?
    <area?shape="poly"?coords="324,167,311,173,297,179,282,182,266,185,250,186,234,185,217,183,202,179,188,173,175,167,
     ?165,159,157,151,151,142,147,132,146,122,148,112,250,123,250,123"?title="本科?=?8,280"?alt=""?
     href="barview.jsp?category=本科&pieIndex=0"/>?
    <area?shape="poly"?coords="307,72,324,80,338,91,347,103,352,117,352,131,347,144,338,156,324,167,250,123,250,123"?title="大專?=?5,700"?alt=""?href="barview.jsp?category=大專&pieIndex=0"/>?
    <area?shape="poly"?coords="261,62,285,65,307,72,250,123,250,123"?title="高中?
     =?1,530"?alt=""?href="barview.jsp?category=高中&pieIndex=0"/>?
    <area?shape="poly"?coords="250,61,261,62,250,123,250,123"?title="高中以下?=?370"?alt=""?href="barview.jsp?category=高中以下&pieIndex=0"/>?
    </map>?


      這就是MAP信息,我們在IMG標簽中加入usemap="#map0"就可以為餅圖的每一部分加入鏈接。

      <五>柱狀圖頁面代碼:(barview.jsp)

    <HTML>?
    <HEAD>?
    <META?http-equiv=Content-Type?content="text/html;?charset=GBK">?
    <TITLE>nacl_zhuang@hotmail.com</TITLE>?
    </HEAD>?

    <body>?

    <%@?page?contentType="text/html;charset=GBK"%>?
    <%@?page?import="org.jfree.chart.ChartFactory,?
    org.jfree.chart.JFreeChart,?
    org.jfree.chart.plot.PlotOrientation,?
    org.jfree.chart.servlet.ServletUtilities,?
    org.jfree.data.category.*"%>?
    <%?
    CategoryDataset?dataset;?
    String?category=request.getParameter("category");?
    category=?new?String(category.getBytes("ISO8859_1"),?"GBK");?
    if(category.equals("本科")||category.equals("高中")||category.equals("大專"))?
    {?
     dataset=getDataSet();?
    }?
    else?if(category.equals("碩士")||category.equals("博士"))?
    {?
     dataset=getDataSet2();?
    }else?
    {?
     dataset=getDataSet3();?
    }?
    String?title=category+"程序員在各城市薪金情況統計";?
    JFreeChart?chart?=?ChartFactory.createBarChart3D(title,?
    "城市",?
    "薪金",?
    dataset,?
    PlotOrientation.VERTICAL,?
    true,?
    false,?
    false);?

    String?filename?=?ServletUtilities.saveChartAsPNG(chart,?500,?300,?null,?session);?
    String?graphURL?=?request.getContextPath()?+?"/servlet/DisplayChart?filename="?+?filename;?
    %>?
    <P?ALIGN="CENTER">?
    <img?src="<%=?graphURL?%>"?width=500?height=300?border=0?usemap="#<%=?filename?%>">?
    </P>?
    <%!?
    private?static?CategoryDataset?getDataSet()?{?
     DefaultCategoryDataset?dataset?=?new?DefaultCategoryDataset();?
     dataset.addValue(2000,?"北京",?"VB");?
     dataset.addValue(1800,?"上海",?"VB");?
     dataset.addValue(2200,?"廣州",?"VB");?
     dataset.addValue(3200,?"北京",?"JAVA");?
     dataset.addValue(3500,?"上海",?"JAVA");?
     dataset.addValue(3600,?"廣州",?"JAVA");?
     dataset.addValue(3300,?"北京",?"DOT?NET");?
     dataset.addValue(3400,?"上海",?"DOT?NET");?
     dataset.addValue(3700,?"廣州",?"DOT?NET");?
     dataset.addValue(2500,?"北京",?"DELPHI");?
     dataset.addValue(2800,?"上海",?"DELPHI");?
     dataset.addValue(3200,?"廣州",?"DELPHI");?
     dataset.addValue(5000,?"北京",?"VC");?
     dataset.addValue(3500,?"上海",?"VC");?
     dataset.addValue(4600,?"廣州",?"VC");?
     return?dataset;?
    }?
    private?static?CategoryDataset?getDataSet2()?{?
     DefaultCategoryDataset?dataset?=?new?DefaultCategoryDataset();?
     dataset.addValue(2000,?"上海",?"VB");?
     dataset.addValue(3000,?"北京",?"JAVA");?
     dataset.addValue(3330,?"上海",?"JAVA");?
     dataset.addValue(3500,?"廣州",?"JAVA");?
     dataset.addValue(3500,?"北京",?"DOT?NET");?
     dataset.addValue(4000,?"上海",?"DOT?NET");?
     dataset.addValue(4800,?"廣州",?"DOT?NET");?
     dataset.addValue(2600,?"北京",?"DELPHI");?
     dataset.addValue(2200,?"上海",?"DELPHI");?
     dataset.addValue(4000,?"北京",?"VC");?
     dataset.addValue(4000,?"上海",?"VC");?
     dataset.addValue(4200,?"廣州",?"VC");?
     return?dataset;?
    }?
    private?static?CategoryDataset?getDataSet3()?{?
     DefaultCategoryDataset?dataset?=?new?DefaultCategoryDataset();?
     dataset.addValue(2100,?"北京",?"VB");?
     dataset.addValue(2200,?"上海",?"VB");?
     dataset.addValue(2100,?"廣州",?"VB");?
     dataset.addValue(3000,?"北京",?"JAVA");?
     dataset.addValue(3200,?"上海",?"JAVA");?
     dataset.addValue(3600,?"廣州",?"JAVA");?
     dataset.addValue(4100,?"北京",?"DOT?NET");?
     dataset.addValue(4200,?"上海",?"DOT?NET");?
     dataset.addValue(4160,?"廣州",?"DOT?NET");?
     dataset.addValue(2400,?"北京",?"DELPHI");?
     dataset.addValue(2600,?"上海",?"DELPHI");?
     dataset.addValue(2500,?"廣州",?"DELPHI");?
     dataset.addValue(5400,?"北京",?"VC");?
     dataset.addValue(5000,?"上海",?"VC");?
     dataset.addValue(5500,?"廣州",?"VC");?
     return?dataset;?
    }?
    %>?
    </body>?
    </html>?

      生成圖片如下:?


    posted @ 2006-12-29 23:36 tory 閱讀(386) | 評論 (0)編輯 收藏

    2006年12月25日 #

    ?
    在用 AJAX 開發的過程中, 不可避免的會遇到中文問題. 很多原來可以通過表單進行 POST 提交的字符, 到了用 AJAX 實現的時候, 就會出現煩人的亂碼和丟特殊字符的現象. 另外服務器端返回值如何解析, 也是一個很煩人的問題. 本文將就個人的一點實踐經驗作出總結, 并給出一個盡量簡單可行, 復用性高的方案. 目的不是替代你喜歡的 AJAX 框架, 而是希望幫助您理解和處理可能遇到的問題.

    開始之前: 首先一個問題就是通常 XMLHttpRequest 默認的編碼都是UTF-8的, 所以我們建議所有頁面, 客戶端和服務器端都使用 UTF-8 作為編碼.

    1. base64 encode 和 decode
    ??? 這個方案依賴于 JavaScript 實現的 base64 編碼/解碼方法, 在客戶端發送參數的時候用 base64 進行編碼, 服務器端通過 base64 進行解碼后還原出原來的字符, 這個解決方案可以滿足需要, 但是有個問題就是一是增加了客戶端代碼量, 還有個大問題就是編碼后的內容比原始內容會大很多, 另外如果找到的 base64 JS 算法不夠標準的話, 服務器端就無法還原原來的值了. 現在網上有很多種 base64 的 JS 實現代碼, 例如如下的一個算法實現:

    <HTML>
    <HEAD>
    <TITLE>Base64</TITLE>
    <script language=javascript>
    var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    var base64DecodeChars = new Array(
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
      -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
      52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
      -1,  0, 1, 2, 3,? 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
      15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
      -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
      41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);
    function base64encode(str) {
      var out, i, len;
      var c1, c2, c3;
      len = str.length;
      i = 0;
      out = "";
      while (i < len) {
    ?c1 = str.charCodeAt(i++) & 0xff;
    ?if(i == len)
    ?{
       out += base64EncodeChars.charAt(c1 >> 2);
       out += base64EncodeChars.charAt((c1 & 0x3) << 4);
       out += "==";
       break;
    ?}
    ?c2 = str.charCodeAt(i++);
    ?if(i == len)
    ?{
       out += base64EncodeChars.charAt(c1 >> 2);
       out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
       out += base64EncodeChars.charAt((c2 & 0xF) << 2);
       out += "=";
       break;
    ?}
    ?c3 = str.charCodeAt(i++);
    ?out += base64EncodeChars.charAt(c1 >> 2);
    ?out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
    ?out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6));
    ?out += base64EncodeChars.charAt(c3 & 0x3F);
      }
       return out;
    }
    function base64decode(str) {
      var c1, c2, c3, c4;
      var i, len, out;
      len = str.length;
      i = 0;
      out = "";
      while (i < len) {
    ?/* c1 */
    ?do {
       c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
    ?} while(i < len && c1 == -1);
    ?if(c1 == -1)
       break;
    ?/* c2 */
    ?do {
       c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
    ?} while(i < len && c2 == -1);
    ?if(c2 == -1)
       break;
    ?out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
    ?/* c3 */
    ?do {
       c3 = str.charCodeAt(i++) & 0xff;
       if(c3 == 61)
     return out;
       c3 = base64DecodeChars[c3];
    ?} while(i < len && c3 == -1);
    ?if(c3 == -1)
       break;
    ?out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
    ?/* c4 */
    ?do {
       c4 = str.charCodeAt(i++) & 0xff;
       if(c4 == 61)
     return out;
       c4 = base64DecodeChars[c4];
    ?} while(i < len && c4 == -1);
    ?if(c4 == -1)
       break;
    ?out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
      }
       return out;
    }
    function utf16to8(str) {
      var out, i, len, c;
      out = "";
      len = str.length;
      for(i = 0; i < len; i++) {
    ?c = str.charCodeAt(i);
    ?if ((c >= 0x0001) && (c <= 0x007F)) {
       out += str.charAt(i);
    ?} else if (c > 0x07FF) {
       out += String.fromCharCode(0xE0 | ((c >> 12) & 0x0F));
       out += String.fromCharCode(0x80 | ((c >> 6) & 0x3F));
       out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
    ?} else {
       out += String.fromCharCode(0xC0 | ((c >> 6) & 0x1F));
       out += String.fromCharCode(0x80 | ((c >> 0) & 0x3F));
    ?}
      }
       return out;
    }
    function utf8to16(str) {
      var out, i, len, c;
      var char2, char3;
      out = "";
      len = str.length;
      i = 0;
      while (i < len) {
    ?c = str.charCodeAt(i++);
    ?switch(c >> 4)
    ?{
      case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7:
       // 0xxxxxxx
       out += str.charAt(i-1);
       break;
      case 12: case 13:
       // 110x xxxx  10xx xxxx
       char2 = str.charCodeAt(i++);
       out += String.fromCharCode(((c & 0x1F) << 6) | (char2 & 0x3F));
       break;
      case 14:
       // 1110 xxxx 10xx xxxx 10xx xxxx
       char2 = str.charCodeAt(i++);
       char3 = str.charCodeAt(i++);
       out += String.fromCharCode(((c & 0x0F) << 12) |
         ((char2 & 0x3F) << 6) |
         ((char3 & 0x3F) << 0));
       break;
    ?}
      }
       return out;
    }

    function doit() {
      var f = document.f
       f.output.value = base64encode(utf16to8(f.source.value))
       f.decode.value = utf8to16(base64decode(f.output.value))
    }
    </script>
    </HEAD>
    <BODY>
    <H1>Base64</H1>
    <FORM NAME="f">
    原碼& lt;BR>
    <TEXTAREA NAME="source" ROWS=4 COLS=60 WRAP="soft"></TEXTAREA><BR><BR>
    Base64 encode<BR>
    <TEXTAREA NAME="output" ROWS=4 COLS=60 WRAP="soft"></TEXTAREA><BR><BR>
    Base64 decode<BR>
    <TEXTAREA NAME="decode" ROWS=4 COLS=60 WRAP="soft"></TEXTAREA><BR><BR>
    <INPUT TYPE=BUTTON VALUE="轉換" ONCLICK="doit()">
    </FORM>
    </BODY>

    在每個表單值被提交之前調用 base64encode, 然后在服務器端調用 base64 解碼器即可.
    在 JSP 中可以通過這樣做來實現:
    ??? ??? sun.misc.BASE64Decoder base64decoder = new sun.misc.BASE64Decoder();
    ??? ??? byte[] data =?base64decoder.decodeBuffer(request.getParameter("input"));
    ??? ??? String result = new String(data, "UTF-8");// 注意建議這里制定字符集來歡迎到原來的字符串

    在這種情況下服務器端返回的字符也可以通過先 base64 編碼的方式傳遞到客戶端, 客戶端之后調用 JS 形式的解碼器即可還原到原來的字符串. 服務器端可以使用 sun.misc.BASE64Encoder (不要用 java.netURLEncoder).

    2.使用 JS 自帶的 escape() & encodeURI() & encodeURIComponent()

    escape() & encodeURI() & encodeURIComponent()這三個函數都可以用來對URI進行encode或過濾特殊字符(#/$&+=?/等)。我的經驗是最好用encodeURIComponent()(需要IE 5.5以上,FireFox當然沒問題),因為對UTF-8支持比較好,不會遇到中文亂碼問題,否則還需要進行編碼轉換,很麻煩的。使用其它兩個函數都會發生丟失特殊字符的問題,例如空格變+號或者空格,引號,&=?等丟失的問題, 至少使用 JSP 作為服務器端的話會發生這種情況, 有興趣的朋友可以將本文最后的例子代碼中的編碼部分修改后做個測試.

    下面是MSDN上對這三個函數的解釋:

    escape(charString)

    The escape method returns a string value (in Unicode format) that contains the contents of charstring. All spaces, punctuation, accented characters, and any other non-ASCII characters are replaced with %xx encoding, where xx is equivalent to the hexadecimal number representing the character. For example, a space is returned as "%20."

    Characters with a value greater than 255 are stored using the %uxxxx format.

    Note?? The escape method should not be used to encode Uniform Resource Identifiers (URI). Use encodeURI and encodeURIComponent methods instead.

    http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthescape.asp

    ?encodeURI(URIString)

    The encodeURI method returns an encoded URI. If you pass the result to decodeURI, the original string is returned. The encodeURI method does not encode the following characters: ":", "/", ";", and "?". Use encodeURIComponent to encode these characters.

    http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthfencodeuri.asp

    encodeURIComponent(encodedURIString)

    The encodeURIComponent method returns an encoded URI. If you pass the result to decodeURIComponent, the original string is returned. Because the encodeURIComponent method encodes all characters, be careful if the string represents a path such as /folder1/folder2/default.html. The slash characters will be encoded and will not be valid if sent as a request to a web server. Use the encodeURI method if the string contains more than a single URI component.

    http://msdn.microsoft.com/library/en-us/script56/html/js56jsmthencodeuricomponent.asp

    3. 使用一個簡便的客戶端數據解析方案

    最偷懶的辦法就是返回一段 HTML 顯示出來了. 至于如果是想帶一些數據, 解析處理的話, 方案很多, 利用 XML 啊, JSON 啊什么的不一而足. 我這里呢就給出一個相當簡便的方案: 使用 JS 內置的 eval 方法來解析. 這個方案是在幫助一個同事想最快的已最短的代碼解析返回的對象的多個變量的時候提出的.

    服務器端返回一個字符串:
    var _dataObject = {
    ?? username : "beansoft",
    ??????? age : 24
    };

    客戶端在得到這個字符串后可以通過下面一段代碼搞定:
    var responseText = xmlhttp.responseText;
    eval(responseText);
    alert("_dataObject.username=" + _dataObject.username);

    好了, 解析出來了!
    如果要傳遞多個變量呢, 就用 var _dataObject1, var _dataObject2...這樣就可以了, 客戶端就依次是 _dataObject1.username, _dataObject2.username...

    等等: 我的變量里寫了特殊字符怎么辦? 例如我用的字符串是 'abc"'', 這時候我不得不拋出殺手锏了, 這就是用 Java 實現的 escape(), unescape() 方法, 其實本例中只需要 escape() 的 Java 版本就可以了(這個方案也幫助另一個同事解決了從JSP端傳遞的變量含有'號結果導致客戶端沒法顯示的問題):

    ??? public static String escape(String src) {
    ??? ??? int i;
    ??? ??? char j;
    ??? ??? StringBuffer tmp = new StringBuffer();
    ??? ??? tmp.ensureCapacity(src.length() * 6);
    ??? ??? for (i = 0; i < src.length(); i++) {
    ??? ??? ??? j = src.charAt(i);
    ??? ??? ??? if (Character.isDigit(j) || Character.isLowerCase(j)
    ??? ??? ??? ??? ??? || Character.isUpperCase(j))
    ??? ??? ??? ??? tmp.append(j);
    ??? ??? ??? else if (j < 256) {
    ??? ??? ??? ??? tmp.append("%");
    ??? ??? ??? ??? if (j < 16)
    ??? ??? ??? ??? ??? tmp.append("0");
    ??? ??? ??? ??? tmp.append(Integer.toString(j, 16));
    ??? ??? ??? } else {
    ??? ??? ??? ??? tmp.append("%u");
    ??? ??? ??? ??? tmp.append(Integer.toString(j, 16));
    ??? ??? ??? }
    ??? ??? }
    ??? ??? return tmp.toString();
    ??? }

    ??? public static String unescape(String src) {
    ??? ??? StringBuffer tmp = new StringBuffer();
    ??? ??? tmp.ensureCapacity(src.length());
    ??? ??? int lastPos = 0, pos = 0;
    ??? ??? char ch;
    ??? ??? while (lastPos < src.length()) {
    ??? ??? ??? pos = src.indexOf("%", lastPos);
    ??? ??? ??? if (pos == lastPos) {
    ??? ??? ??? ??? if (src.charAt(pos + 1) == 'u') {
    ??? ??? ??? ??? ??? ch = (char) Integer.parseInt(src
    ??? ??? ??? ??? ??? ??? ??? .substring(pos + 2, pos + 6), 16);
    ??? ??? ??? ??? ??? tmp.append(ch);
    ??? ??? ??? ??? ??? lastPos = pos + 6;
    ??? ??? ??? ??? } else {
    ??? ??? ??? ??? ??? ch = (char) Integer.parseInt(src
    ??? ??? ??? ??? ??? ??? ??? .substring(pos + 1, pos + 3), 16);
    ??? ??? ??? ??? ??? tmp.append(ch);
    ??? ??? ??? ??? ??? lastPos = pos + 3;
    ??? ??? ??? ??? }
    ??? ??? ??? } else {
    ??? ??? ??? ??? if (pos == -1) {
    ??? ??? ??? ??? ??? tmp.append(src.substring(lastPos));
    ??? ??? ??? ??? ??? lastPos = src.length();
    ??? ??? ??? ??? } else {
    ??? ??? ??? ??? ??? tmp.append(src.substring(lastPos, pos));
    ??? ??? ??? ??? ??? lastPos = pos;
    ??? ??? ??? ??? }
    ??? ??? ??? }
    ??? ??? }
    ??? ??? return tmp.toString();
    ??? }

    這樣, 在服務器端的時候可以變成:
    <%
    String username = "'abc\"''";// 其實這個普通子串轉換成 Java 語言中的字符串也有工具可以用的, 例如本人開發的 Native2JavaString, 改日再講.
    %>
    var _dataObject = {
    ?? username : "<%=escape(username)%>",
    ??????? age : 24
    };
    客戶端呢, 就可以簡單的來JS自帶的 unescape() 函數來取出原來的字符串:
    var responseText = xmlhttp.responseText;
    eval(responseText);
    alert("_dataObject.username=" + unescape(_dataObject.username));
    就是服務器端用 Java 寫的 escape(), 客戶端呢就用 JS 自帶的 unescape().

    4.?實例代碼
    好了, 說了這么多, 就推出個人的解決方案吧. 簡單的講就是我寫了一個腳本對象 AjaxFormer, 使用的是 escape來自動的將原來的 POST/GET 方式的提交代碼自動的轉換成 AJAX 的方式.

    /**
    ?* @constructor
    ?* This is a ajax form helper class.
    ?*
    ?* @param form - the document form
    ?* @param resultDivId - the result div id
    ?*/
    function AjaxFormer (form, resultDivId);

    構造器的第一個參數是個 form 對象, 第二個是個可選的結果 DIV 對象, 也就是說你可以指定服務器端返回的 HTML 代碼顯示的地方, 如果保持為空的話, 那么返回的 HTML?會被附加到文檔的末尾. 本對象有一個名字為 ajaxSubmitForm() 的方法來自動的遍歷所有表單元素, 然后將結果拼成一個字符串, 最后根據原來的表單的提交方式(get/post)來自動再客戶端用 AJAX 模擬提交這個表單到原來的表單的 action 屬性所指定的頁面中去.

    用法示例:
    <html>

    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
    <title>AJAX Form Submit Test</title>
    <script src='ajax_common.js'></script>

    </head>

    <body>
    <h3>AJAX Form Submit Test</h3>
    Fill the form and then click submit
    <form method="POST" id="form1" name="form1"
    action="form_action.jsp"
    onSubmit="former.ajaxSubmitForm();return false;">
    ??? <p><input type="hidden" name="hidden1" value="hiddenValue">
    ??? text:<input type="text" name="textf&1" size="20" value="text1">
    ??? checkbox:<input type="checkbox" name="checkbox1" value="ON" checked>
    ??? radio:<input type="radio" value="V1" checked name="radio1">
    ??? select:<select size="1" name="select1">
    ??? <option selected value="option1">D1</option>
    ??? </select>
    ??? <br>
    ??? <br>
    ??? <input type="submit" name="B1" value="submit">
    ??? <input type="reset" name="B2" value="reset">
    ??? </p>
    </form>

    <script type="text/javascript">
    var former = new AjaxFormer($('form1'));
    </script>
    </body>

    </html>

    紅色的字體就是您從一個非 AJAX 的表單提交改變成一個 AJAX 的表單提交所需要做的工作, 看上去夠簡單吧?

    運行時的效果如圖所示:
    http://www.tkk7.com/images/blogjava_net/beansoft/18680/o_ajaxFormer.png

    下載本文的源碼:?

    AJAXFormer.zip4KB

    將源碼解壓縮到JSP服務器的任意目錄下即可, 例如 $TOMCAT_HOME\webapps\ROOT 下, 然后在瀏覽器里鍵入:
    http://localhost:8080/ajax_form_submit.htm

    作者: beansoft@126.com?2006.12.25

    posted @ 2006-12-25 21:01 tory 閱讀(763) | 評論 (1)編輯 收藏

    2006年12月24日 #

    ? 在這篇 ASP.NET 中Session 實現原理淺析[2] 狀態管理器 Blog中, Flier 老大向大家推薦了Java中幾種Cache的實現,于是我就按圖索驥,首先是找到OSCache的老家: OpenSymphony 。哇,這里的資源真多啊,簡直就是一個寶藏。為什么原來就沒有發現呢?感謝Flier老大了!
    ??????? 在OpenSymphony的主頁上,我看到了兩個讓我覺得很親切的項目:WebWork和Quarts。WebWork作為一個實現了IOC的輕量級Web Application Framework一直備受開發者的青睞;而Quarts更是在異步信息處理上大展拳腳了。看到了老朋友,我就只好暫時將新歡OSCache擺一邊,找出我很久沒有聯系的老貓(Tomcat)去跟WebWork聊嗑了。
    ??????? 做好了一切的準備工作之后[1],我嘗試著做一個登陸注冊的功能,以體驗一下WebWork。跟其他的Web應用一樣,我們首先要建立一個標準的WEB-INF目錄(所謂標準就是目錄下面包含lib和classes子目錄以及Web.xml文件),接著在WEB-INF/lib下面放上WebWork所需要的.jar文件并在WEB-INF目錄下建立Web.xml文件[2]。在完成了這些千篇一律的工作之后,我仔細的看了一下Web.xml中內容,它里面只定義了一個servlet:webwork,其對應的class為com.opensymphony.webwork.dispatcher.ServletDispatcher,然后由webwork這個servlet去處理所有.action的請求。看到這里,我暗自竊喜,這跟我熟悉的WAF框架是十分類似的,只不過MainServlet變成了ServletDispatcher,.do的請求變成了.action而已。
    ??????? 接下來,就是到classes目錄下建立xwork.xml文件,這個文件跟WAF中的mappings.xml很相似,因為對.action的處理都是在這里被定義的,而WAF中關于.do的處理則定義在mapping.xml中。但是也有一些我并不清楚的東西,如package和Interceptor。在classes目錄下還要建立一個validator.xml文件,但是這個并不是必需的。做好了這些準備工作之后,就真正開始WebWork的體驗之旅了。
    ?????? ?1、建立一個index.jsp(以下為主要部分):
    ?<form?action="Login.action"?method="post">
    ??
    < table? cellspacing =0? width ="100%" >
    ????
    < tr >
    ??????
    < td > Login?ID:
    ????????
    < input? type ="text" ?name ="loginId" ?width =100? /> &nbsp;&nbsp;

    ??????????Password:
    ????????
    < input? type ="password" ?name ="loginPassword" ?width =100? /> &nbsp;&nbsp;
    ????????
    < input? type ="submit" ?value ="Login" ? />
    ??????
    </ td >
    ??????
    < td? align =right>
    ?????????
    Hello,?<ww:property?value ="loginId" ? />

    ??????
    </ td >
    ????
    </ tr >
    ??
    </ table >
    </ form > < SPAN>

    ????????需要注意的地方就是form的action屬性的寫法了;
    ??????? 2、在xwork.xml中增加相應的處理action的節點

    ?<action?name="Login"?class="fantasysoft.webwork.Login">
    ???
    < result? name ="error" ?type ="dispatcher" > index.jsp </ result >
    ???
    < result? name ="success" ?type ="dispatcher" > index.jsp </ result >
    </ action >

    ????????這里需要注意的是action節點中name的值要與index.jsp中定義的action的名字要嚴格匹配,對于大小 寫是敏感的。在action節點中還包含了節點,以說明處理action之后會可能出現的不同結果(name)和相應的處理方式(type)。譬如說,如果Login的這個action處理success了,則使用dispatcher將結果分(dispatch)到相應的頁面。而在WAF框架中并沒有這樣的定義,因為在默認情況下如果不成功則會返回當前頁面,不過可以定義FlowHandler,并擁有類似的功能且更加靈活;
    ??????? 3、實現類Login的代碼:

    package?fantasysoft.webwork;

    import?com.opensymphony.xwork.ActionSupport;

    public ? class ?Login?extends?ActionSupport
    {
    ????
    private
    ?String?loginId;
    ????
    private
    ?String?loginPassword;

    ????
    public ?String?getLoginPassword()?
    {
    ????????
    return
    ?loginPassword;
    ????}

    ????
    public ? void ?setLoginPassword(String?loginPassword)? {
    ????????
    this .loginPassword? =
    ?loginPassword;
    ????}

    ????
    public ?String?getLoginId()? {
    ????????
    return
    ?loginId;
    ????}

    ????
    public ? void ?setLoginId(String?loginId)? {
    ????????
    this .loginId? =
    ?loginId;
    ????}

    ????
    public ?String?execute()?throws?Exception {?????????????
    ????????
    if ?(!checkUserId() )? return
    ?ERROR;? // checkUserId is the method that will be implemented
    ????????????
    else ? return
    ?SUCCESS;
    ????}

    }

    ????????在代碼中,你會發現有兩個繼承變量ERROR與SUCCESS。這兩個變量是定義在Action這個接口的,而ActionSupport則實現了Action接口。在接口Action的代碼中,我們可以看到ERROR = "error"、SUCCESS = "success"。我們可以發現這兩個變量的值與xwork.xml中result子節點中的name的值是相匹配。除此之外,在代碼中,我也找不到了原來在開發中經常要用到的一個API:getParameter。事實上,將表單中數據析取出來的工作是由webwork這個唯一定義的servlet去完成的,而這個類會調用Login類中set的方法將用戶輸入的數據賦給Login類的屬性:loginId和loginPassword。在這里,我們也要跟前面index.jsp中的包含的標簽聯系起來。當數據被dispatch回index.jsp的時候,在index.jsp頁面render的過程中是調用了get的方法去獲取相應的數據的。
    ??????? 最后,我們可以總結一下,整個Web應用程序的處理流程了:
    ??????? 首先,當用戶提交了表單(form)至Login.action后,由web.xml中定義的唯一的一個servlet:webwork去處理這個請求。webwork會以action的name:Login到xwork.xml中尋找相應的處理action的類,于是就找到了fantasysoft.webwork包中的Login類,由Login類中的execute方法來處理提交的form的數據了;
    ????????然后,根據execute方法的返回值,再到xwork.xml中對應的action節點中去找匹配的result子節點;
    ??????? 最后,根據result子節點的定義,將處理結果分發(dispatch)或者重定向(redirect)至下一個頁面[3]

    ????????
    ????????[1] 準備工作可以參考
    WebWork?Getting Started
    ??????? [2]?web.xml文件的具體內容,可以參考
    WebWork Tutorial Lesson 2 ?
    ??????? [3] 對于result的Type的更多介紹,可以參考
    WebWork Tutorial Lesson 3 ?????

    /SPAN>
    posted @ 2006-12-24 22:42 tory 閱讀(161) | 評論 (0)編輯 收藏

    2006年12月21日 #

         摘要: 一 Ajax-- 整合的力量 2005 年,伴隨著 Web2.0 的東風, Ajax 逐漸進入國內開發人...  閱讀全文
    posted @ 2006-12-21 22:40 tory 閱讀(295) | 評論 (0)編輯 收藏

    2006年12月18日 #

    重載overloading和覆寫overriding哪個更早執行-- visitor幫助篇

    重載overloading和覆寫overriding哪個更早執行--?? visitor幫助篇
    一:問題提出
    雖然我們經常寫程序用到重載和覆寫,但是很少會考慮他們的執行順序。下邊的內容就是關于,他們同時出現時
    哪個先起作用:
    二:問題分析
    Java是"動態單分派靜態多分派語言",這個定義已經多次提起,如果你不了解這些概念,看這里"visitor模式準備"
    所以就注定了重載(靜態多分派)要早于覆寫(動態單分派),因為靜態分派是編繹期實現的,動態分派是執行期實現的。
    三:驗證
    簡單驗證一下,順變提高記憶

    ?1 public ? class ?Parent? {
    ?2 ???? public ? void ?run(Object?o) {
    ?3 ????????System.out.println( " in?Parent?+param:object " );
    ?4 ????}

    ?5 ???? public ? void ?run( int ?i) {
    ?6 ????????System.out.println( " in?Parent?+?param:int " );
    ?7 ????}

    ?8 }

    ?9
    10 public ? class ?Child? extends ?Parent? {
    11
    12 ???? public ? void ?run(Object?o) {
    13 ????????System.out.println( " in?Child?+param:Object " );
    14 ????}

    15 ???? public ? void ?run(String?str) {
    16 ????????System.out.println( " in?Child?+?param:String " );
    17 ????}

    18 ???? public ? static ? void ?main(String[]?args)? {
    19 ????????Parent?p? = ? new ?Child();
    20 ????????String?str? = ? new ?String();
    21 ????????p.run(str);
    22 ????}

    23 }


    運行結果是什么?
    in Child +param:Object
    inChild是確認的,但是為什么是object,而不是String,我們放入的就是String啊。
    首先來分析執行過程。
    定義韋類型Parent p在執行run(Str)的時候,

    1,如果是先執行重載,然後是執行覆寫的過程
    重載時因為找不到對應的String參數的函數,所以定位到接受父類的run(Object o)函數,
    覆寫時因為傳入時父類告訴子類的對象類型是Object,所以執行run(Object o);
    正是我們看到的結果,所以在Java中執行的順序是這樣的。

    2,為了對比,說一下先覆寫后重載的過程
    如果是先覆寫,再重載
    覆寫時因為確定對象實際是子類,所以直接覆寫到Child,然後重載,發現有對應的String為參數的函數
    執行,run(String str);
    應該輸出的結果:in Child +param:String
    但我們看到結果顯然是1,所以驗證了Java是先重載后覆寫的。


    最后的部分:

    看完本文,如果你對visitor模式有更多的興趣,想了解更多請看如下幾篇文章。
    1,靜態分派,動態分派,多分派,單分派 --------------?? visitor模式準備
    2,訪問差異類型的集合類 ------------------------?? visitor模式入門
    3,visitor模式理論及學術概念-------------------?? visitor模式深入
    4,visitor模式和其它模式的比較和關系-------------?? visitor模式總結?
    5,重載overloading和覆寫overriding哪個更早執行--?? visitor幫助篇 (本文)
    雖然排列順序是1,2,3,4,5 但是我個人建議的學習方式是2,1,3,4,5因為這個順序更方便一般人理解

    posted @ 2006-12-18 22:38 tory 閱讀(243) | 評論 (0)編輯 收藏

    2006年12月10日 #

    所謂RCP,就是Rich Client Platform的縮寫,即富客戶平臺,是Eclipse進化的產物(自3.0版以后出現),是Eclipse組織向用戶提供的強大的開放性開發平臺,能夠使用戶方便地創建自己的基于Eclipse的應用程序,并且這些應用程序能夠得到Eclipse的底層支持。更重要的是,我們可以利用Java創建象Eclipse這么漂亮的桌面程序。

      我相信,在未來的幾年里,RCP一定會變得非常流行。使用RCP,我們可以開發界面象Eclipse這樣漂亮的桌面程序,比如醫院管理系統啊、CAD軟件等等。遺憾的是,目前在國內基本上找不到關于RCP的中文資料,我們只能通過自己的探索來為我們的程序添加我們想要的功能。

      下面讓我們一步一步來建立一個Eclipse RCP程序,下面的內容可以說在Google上一搜一大把,有些人會覺得乏味,但是沒關系,這只是一個快速的起步。

      選擇“新建--項目”,選擇“插件項目”:
    rcp00.JPG

    rcp01.JPG

    點下一步,輸入項目名稱,選擇Eclipse版本,我這里選擇的是3.2:
    rcp02.JPG

      點下一步,插件標識和插件名稱可以更改,其他的內容都可以保持默認,一定要記得選中富客戶機應用程序支持:
    rcp03.JPG

      點下一步,選中一個模板,這里選一個最簡單的,到時候看源代碼的時候便于理解:
    rcp04.JPG

      點下一步,改一下應用程序標題:
    rcp05.JPG

      點完成,我們可以在項目上面點右鍵,選擇按Eclipse程序運行,就可以看到效果了:
    rcp16.JPG

    rcp17.JPG

      在這個程序中,窗口上顯示的是一個透視圖,透視圖中含有一個編輯器區域,以后,我們可以逐步為這個程序添加菜單、工具條和為這個透視圖添加視圖、編輯器等等。

      現在,這個程序只能在Eclipse環境下運行,而RCP的目標是創建可以獨立運行的應用程序,我們的事情還沒完呢。下一步,在項目上點右鍵,創建產品配置文件:
    rcp06.JPG

      輸入產品配置文件名:

    rcp07.JPG

      生成的產品配置文件在編輯器中打開,應該是這個樣子的:
    rcp09.JPG

      剛開始,上面的幾個文本框都是空的,點新建按鈕之后,彈出如下的對話框,輸入產品名稱后,點完成就行了。

    rcp08.JPG

      點擊配置文件中的“啟動程序”,我們可以試著啟動我們的RCP程序。結果呢,會出錯。原因很簡單,因為我們沒有為我們的程序選中它依賴的插件。

    ?  選中配置文件的“配置”選項卡,添加以下幾個依賴項,記住,一定要把我們自己,也就是com.blogjava.youxia.rcp_start加進依賴項,否則會出錯。最開始的時候,就是這么一點小問題,讓我浪費了幾天時間。
    rcp10.JPG

      再點擊添加必須的插件,自動添加其它的依賴項。

      再下一步,設置項目的構建路徑,如下圖:
    rcp11.JPG

      下一步,導出我們的程序:
    rcp12.JPG

    rcp13.JPG

      點下一步,輸入我們程序導出的目錄,如下圖:
    rcp14.JPG

      點完成按鈕之后,我們的程序就導出到我們的指定的目錄中了,打開這個目錄,可以看到一個類似eclipse的程序圖標,雙擊運行,效果如下圖:rcp15.JPG

      最后,需要說明兩點:第一,如果希望生成的程序有自己的圖標,可以在產品配置文件中的最后兩個配置文件中設置;第二,生成的程序應該是沒有菜單欄的,因為我的Eclipse安裝了MyEclipse,所以導出的程序就多了兩個菜單。

      好了,快速起步就到這里了,以后再仔細研究生成的代碼和為我們的程序添加功能。
    posted @ 2006-12-10 08:59 tory 閱讀(224) | 評論 (0)編輯 收藏

    2006年11月25日 #

    JAXP 專述

    Sun 的 Java API for XML 語法分析

    ?

    ?

    未顯示需要 JavaScript 的文檔選項


    ?


    級別: 初級

    Brett McLaughlin , Enhydra 策略顧問, Lutris Technologies

    2000 年 11 月 01 日

    這是篇細探 JAXP,Sun 的 Java API for XML 的文章,幫助解除了有關 JAXP 本質和服務目的的疑惑。本文講解了 JAXP 的基本概念,演示 XML 語法分析為什么需要 JAXP,并顯示如何輕易更改 JAXP 使用的語法分析器。本文還進一步講述了 SAX 和 DOM 這兩個流行的與 JAXP 相關的 Java 和 XML API。

    Java 和 XML 在每一個技術領域都制造了新聞,并且對于軟件開發人員來說,似乎是 1999 年和 2000 年最重要的發展。結果,Java 和 XML API 的數量激增。其中兩個最流行的 DOM 和 SAX 還引起極大興趣,而 JDOM 和數據綁定 API 也接踵而來。只透徹理解這些技術中的一個或兩個就是一項艱巨任務,而正確使用所有這些技術就會使您成為專家。但在去年,另一個 API 給人留下了深刻印象,它就是 Sun 的 Java API for XML,通常稱為 JAXP。如果考慮到 Sun 在其平臺上還沒有任何特定于 XML 的產品,那么這個進展就不足為奇。而令人驚奇的是人們對 JAXP 了解的缺乏。多數使用它的開發人員在他們所用的這個 API 的概念理解上都有錯誤。

    什么是 JAXP?

    本文假設您有 SAX 和 DOM 的基本知識。這里實在沒有足夠篇幅來解釋 SAX、DOM 和 JAXP。如果您是 XML 語法分析的新手,那么可能要通過聯機資源閱讀 SAX 和 DOM,或者瀏覽我的書。( 參考資源 一節中有至 API 和我的書的鏈接。)獲得基本知識后再看本文會比較好。





    回頁首


    API 還是抽象?

    在講解代碼之前,介紹一些基本概念很重要。嚴格地說,JAXP 是 API,但是將其稱為抽象層更準確。它不提供處理 XML 的新方式,不補充 SAX 或 DOM,也不向 Java 和 XML 處理提供新功能。(如果在這點上理解有誤,則本文正好適合您!)它只是使通過 DOM 和 SAX 處理一些困難任務更容易。如果在使用 DOM 和 SAX API 時遇到特定于供應商的任務,它還使通過獨立于供應商的方式處理這些任務成為可能。

    雖然要分別講述所有這些特性,但是真正需要掌握的是:JAXP 不提供語法分析功能 !沒有 SAX、DOM 或另一個 XML 語法分析 API,就 無法分析 XML 語法 。有很多人曾讓我將 DOM、SAX 或 JDOM 與 JAXP 進行對比。但進行這些對比是不可能的,因為前三個 API 與 JAXP 的目的完全不同。SAX、DOM 和 JDOM 都分析 XML 語法。而 JAXP 卻提供到達這些語法分析器和結果的方式。它自身不提供分析文檔語法的新方法。如果要正確使用 JAXP,則一定要弄清這點。這將使您比其它 XML 開發人員領先一大截。

    如果仍然懷疑(或認為我故弄玄虛),請從 Sun 的 Web 站點下載 JAXP 分發(請參閱 參考資料 一節),然后就會知道基本 JAXP 是什么。在包括的 jar ( jaxp.jar ) 中 只有六個類 !這個 API 會有多難哪?所有這些類( javax.xml.parsers 包的一部分)都位于現有語法分析器之上。這些類中的兩個還用于錯誤處理。JAXP 比人們想象的要簡單得多。那么,為什么還感到困惑哪?





    回頁首


    Sun 的 JAXP 和 Sun 的語法分析器

    JAXP 下載時包括 Sun 的語法分析器。所有 parser 器類作為 com.sun.xml.parser 包和相關子包的一部分位于 parser.jar 檔案中。應該知道,該語法分析器(代碼名為 Crimson) 是 JAXP 自身的一部分。它是 JAXP 版本的一部分,但不是 JAXP API 的一部分。令人困惑嗎?有一點。換這種方式想想:JDOM 與 Apache Xerces 語法分析器一起提供。該語法分析器不是 JDOM 的一部分,但由 JDOM 使用,所以包括它,以確保 JDOM 可以單獨使用。JAXP 也是如此,但不象 JDOM 那樣好表達:JAXP 與 Sun 的語法分析器一起提供,以便可以立即使用。但是,很多人將 Sun 的語法分析器中包括的類當成 JAXP API 的一部分。例如,新聞組中一個常見的問題是:“怎樣使用 JAXP 中的 XMLDocument 類?其目的是什么?”這個答案可有些復雜。

    首先, com.sun.xml.tree.XMLDocument 類不是 JAXP 的一部分。它是 Sun 語法分析器的一部分。所以,這個問題從一開始就給人以誤導。其次,JAXP 的整個意義在于在處理語法分析器時提供供應商獨立性。使用 JAXP 的同一代碼可以與 Sun 的 XML 語法分析器、Apache 的 Xerces XML 語法分析器和 Oracle 的 XML 語法分析器一起使用。而使用特定于 Sun 的類是個壞主意。這與 JAXP 的整個意義相背離。現在看出來這個問題怎樣混淆概念了嗎?語法分析器和 JAXP 發行版本(至少是 Sun 的版本)中的 API 被混為一談,開發人員將其中一個的類和特性當成是另一個的了,反之亦然。





    回頁首


    舊和新

    關于 JAXP,最后需要指出的是:使用 JAXP 有一些缺陷。例如,JAXP 只支持 SAX 1.0 和 DOM 第一層規范。SAX 2.0 從 2000 年 5 月起就完成,DOM 第二層規范支持甚至在大多數語法分析器中存在更長時間。DOM 第二層規范還沒有完成,但確實足夠穩定以用于生產。這兩個 API 的新版本都有重大改進,最明顯的是對 XML 名稱空間的支持。該支持還允許“XML Schema 確認”,這個與 XML 相關的另一熱門技術。公平地說,當 JAXP 發布 1.0 最終發行版時,SAX 2.0 和 DOM 第一層規范都還沒有完成。但是,由于沒有包括這些新版本,確實為開發人員帶來很大不便。

    還可以使用 JAXP,但是也可以等待 JAXP 1.1,它支持 SAX 2.0 和 DOM第二層規范 。否則,將發現,JAXP 提供的優點以 SAX 和 DOM 最新版本中的功能為代價,并使應用程序更加難以編碼。無論是否等待下一個 JAXP 發行版,都要留意這個問題。如果將 JAXP 與語法分析器一起使用,而語法分析器支持的 DOM 和 SAX 版本比 JAXP 支持的要高,則可能會有類路徑問題。所以,事先留意一下,并且,一旦有 JAXP 1.1,馬上升級。基本理解 JAXP 之后,讓我們看一下 JAXP 依賴的 API:SAX 和 DOM。





    回頁首


    從 SAX 開始

    SAX (Simple API for XML)是用于處理 XML 的事件驅動方法。它基本由許多回調函數組成。例如,每當 SAX 語法分析器遇到元素的開始標記時就調用 startElement() 。對于字符串,將調用 characters() 回調函數,然后在元素結束標記處調用 endElement() 。還有很多回調函數用于文檔處理、錯誤和其它詞匯結構。現在知道這是怎么回事了。SAX 程序員實現一個定義這些回調函數的 SAX 接口。SAX 還實現一個名為 HandlerBase 的類,該類實現所有這些回調函數,并提供所有這些回調方法的缺省空實現。(提到這一點是因為它在后面講到的 DOM 中很重要。)SAX 開發人員只需擴展這個類,然后實現需要插入特定邏輯的方法。所以,SAX 的關鍵在于為這些不同的回調函數提供代碼,然后允許語法分析器在適當的時候觸發這些回調函數中的每一個。

    因此,典型的 SAX 過程如下:

    • 用特定供應商的語法分析器實現創建一個 SAXParser 實例
    • 注冊回調實現(例如,通過使用擴展 HandlerBase 的類)
    • 開始進行語法分析,然后在觸發回調實現時等待

    JAXP 的 SAX 組件提供執行所有這些步驟的簡單方式。如果沒有 JAXP,SAX 語法分析器要直接從供應商類(如 org.apache.xerces.parsers.SAXParser )進行實例化,或者必須使用名為 ParserFactory 的幫助類。第一個方法的問題很明顯:不獨立于供應商。第二個方法的問題在于類廠需要一個自變量,即要使用的語法分析器類的字符串名稱(還是那個 Apache 類 org.apache.xerces.parsers.SAXParser )。可以通過將不同語法分析器作為 String 傳遞來更改語法分析器。使用這種方法不必更改任何 import 語句,但是還是要重新編譯類。這顯然不是最佳解決方案。如果能夠不重新編譯類而更改語法分析器,可能會簡單得多,是不是這樣呢?

    JAXP 提供了更好的替代方法:它允許將語法分析器作為 Java 系統屬性來提供。當然,當從 Sun 下載版本時,將得到使用 Sun 語法分析器的 JAXP 實現。可以從 Apache XML Web 站點下載在 Apache Xerces 上構建其實現的相同 JAXP 接口。因此(無論哪一種情況),更改正在使用的語法分析器需要更改類路徑設置,即從一種語法分析器實現更改到另一個,但是 要求重新編譯代碼。這就是 JAXP 的魔力,或抽象性。

    SAX 語法分析器一瞥

    JAXP SAXParserFactory 類是能夠輕易更改語法分析器實現的關鍵所在。必須創建這個類的新實例(等一會將講到)。創建新實例之后,類廠提供一個方法來獲得支持 SAX 的語法分析器。在內部,JAXP 實現處理依賴于供應商的代碼,使您的代碼不受影響。這個類廠還提供其它一些優秀特性。

    除創建 SAX 語法分析器實例的基本工作之外,類廠還允許設置配置選項。這些選項影響所有通過類廠獲得的語法分析器實例。JAXP 1.0 中兩個可用的功能是設置名稱空間敏感性 ( setNamespaceAware (boolean awareness)),和打開確認 ( setValidating (boolean validating))。請記住,一旦設置了這些選項,在調用該方法之后,它們將影響 所有從 類廠獲得的實例。

    設置了類廠之后,調用 newSAXParser() 將返回一個隨時可用的 JAXP SAXParser 類實例。這個類封裝了一個下層的 SAX 語法分析器(SAX 類 org.xml.sax.Parser 的實例)。它還防止向語法分析器類添加任何特定于供應商的附加功能。(還記得以前對 XmlDocument 的討論嗎?)這個類可以開始進行實際的語法分析。以下清單顯示如何創建、配置和使用 SAX 類廠。


    清單 1. 使用 SAXParserFactory
    												
    														import java.io.File;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.io.Writer;
    
    // JAXP
    import javax.xml.parsers.FactoryConfigurationError;
    import javax.xml.parsers.ParserConfigurationException;
    import javax.xml.parsers.SAXParserFactory;
    import javax.xml.parsers.SAXParser;
    
    // SAX
    import org.xml.sax.AttributeList;
    import org.xml.sax.HandlerBase;
    import org.xml.sax.SAXException;
    
    public class TestSAXParsing {
    
     public static void main(String[] args) {
     try {
     if (args.length != 1) {
     System.err.println ("Usage: java TestSAXParsing [filename]");
     System.exit (1);
     }
    
     // 獲得SAX 語法分析器類廠
     SAXParserFactory factory = SAXParserFactory.newInstance();
    
     //設置設置名稱空間敏感性選項,關掉確認選項
     factory.setValidating(true);
     factory.setNamespaceAware(false);
    
     SAXParser parser = factory.newSAXParser();
     parser.parse(new File(args[0]), new MyHandler());
    
     } catch (ParserConfigurationException e) {
     System.out.println("The underlying parser does not support " +
     " the requested features.");
     } catch (FactoryConfigurationError e) {
     System.out.println("Error occurred obtaining SAX Parser Factory.");
     } catch (Exception e) {
     e.printStackTrace();
     }
     }
    }
    
    class MyHandler extends HandlerBase {
     //通過 DocumentHandler, ErrorHandler等實現的SAX回調函數
    }
    
    												
    										

    請注意,在這段代碼中,在使用類廠時可能發生兩個特定于 JAXP 的問題:無法獲得或配置 SAX 類廠,以及無法配置 SAX 語法分析器。當無法獲得 JAXP 實現中指定的語法分析器或系統屬性時,通常會發生第一個問題 FactoryConfigurationError 。當正在使用的語法分析器中的特性不可用時,會發生第二個問題 ParserConfigurationException 。這兩個問題都容易處理,應該不會對 JAXP 的使用造成任何困難。

    在獲得類廠、關閉名稱空間并打開“確認”之后,將獲得 SAXParser ,然后開始語法分析。請注意, SAX 語法分析器的 parse() 方法取得前面提到的 SAX HandlerBase 類的一個實例。(可以通過完整的 Java 清單 查看該類的實現 。)還要傳遞要進行語法分析的文件。但是, SAXParser 所包含的遠不止這一個方法。

    使用 SAX 語法分析器

    獲得 SAXParser 類的實例之后,除了向語法分析器傳遞 File 進行語法分析之外,還可以用它做更多的事。由于如今大型應用中的應用程序組件之間通信方式,“對象實例創建者就是其使用者”這樣的假定并不總是安全的。換句話說,一個組件可能創建 SAXParser 實例,而另一組件(可能由另一開發人員編碼)可能需要使用那個實例。由于這個原因,提供了一些方法來確定語法分析器的設置。執行此任務的兩個方法是 isValidating() ,它通知調用程序:語法分析器將要、或不要執行“確認”,以及 isNamespaceAware() ,它返回一個指示,說明語法分析器可以或不可以處理 XML 文檔中的名稱空間。雖然這些方法能提供有關語法分析器可以執行功能的信息,但是無法更改這些特性。必須在語法分析器類廠級別執行該操作。

    另外,有多種方法來請求對文檔進行語法分析。除了只接受 File 和 SAX HandlerBase 實例,SAXParser 的 parse() 方法還能以 String 形式接受 SAX InputSource 、Java InputStream 或 URL,所有這些都要與 HandlerBase 實例一起提供。所以,不同類型的輸入文檔可以用不同方式的語法分析來處理。

    最后,可以直接通過 SAXParser 的 getParser() 方法獲得和使用下層的 SAX 語法分析器( org.xml.sax.Parser 的實例)。獲得這個下層實例之后,就可以獲得通常的 SAX 方法。下一個清單顯示 SAXParser 類(這個 JAXP 中 SAX 語法分析的核心類)的各種使用示例。


    清單 2. 使用 JAXP SAXParser
    												
    														 //獲得SAXP的一個實例
     SAXParser saxParser = saxFactory.newSAXParser();
    
     //查看是否支持 Validate 選項
     boolean isValidating = saxParser.isValidating();
    
     //查看是否支持 namespace 選項
     boolean isNamespaceAware = saxParser.isNamespaceAware();
    
     // 運用一個File 和一個SAX HandlerBase 的實例進行多種形式的語法分析
     saxParser.parse(new File(args[0]), myHandlerBaseInstance);
    
     // 運用一個 SAX InputSource實例 和一個 SAX HandlerBase 實例
     saxParser.parse(mySaxInputSource, myHandlerBaseInstance);
    
     //運用一個 InputStream 實例和一個SAX HandlerBase 實例
     saxParser.parse(myInputStream, myHandlerBaseInstance);
    
     // 運用一個 URI 和一個SAX HandlerBase 實例
     saxParser.parse("http://www.newInstance.com/xml/doc.xml", myHandlerBaseInstance);
    
     //獲得底層的(封裝)SAX 語法分析器 
     org.xml.sax.Parser parser = saxParser.getParser();
    
     //利用底層的語法分析器
     parser.setContentHandler(myContentHandlerInstance);
     parser.setErrorHandler(myErrorHandlerInstance);
     parser.parse(new org.xml.sax.InputSource(args[0]));
    
    												
    										

    目前為止,關于 SAX 已經講了很多,但是還沒有揭示任何不尋常或令人驚奇的東西。事實上,JAXP 的功能很少,特別是當 SAX 也牽涉進來時。這很好,因為有最少的功能性意味著代碼可移植性更強,并可以由其他開發人員與任何與 SAX 兼容的 XML 語法分析器一起使用,無論是免費(通過開放源碼,希望如此)還是通過商業途徑。就是這樣。在 JAXP 中使用 SAX 沒有更多的東西。如果已經知道 SAX,那么現在已經掌握大約 98% 的內容。只需學習兩個新類和兩個 Java 異常,您就可以開始了。如果從沒使用過 SAX,那也很簡單,現在就可以開始。





    回頁首


    處理 DOM

    如果要休息以迎接 DOM 挑戰,那么先別休息。在 JAXP 中使用 DOM 的過程與 SAX 幾乎相同,所要做的全部只是更改兩個類名和一個返回類型,這樣就差不多了。如果理解 SAX 的工作原理和 DOM 是什么,則不會有任何問題。

    DOM 和 SAX 的主要差異是它們的 API 結構。SAX 包含一個基于事件的回調函數集,而 DOM 有一個內存中的樹狀結構。換句話說,在 SAX 中,從不需要操作數據結構(除非開發人員手工創建)。因此,SAX 不提供修改 XML 文檔的功能。而 DOM 正好提供這種類型的功能。 org.w3c.dom.Document 類表示 XML 文檔,它由表示元素、屬性和其它 XML 結構的 DOM 節點 組成。所以,JAXP 無需觸發 SAX 回調,它只負責從語法分析返回一個 DOM Document 對象。

    DOM 語法分析器類廠一瞥

    基本理解 DOM 以及 DOM 和 SAX 的差異之后,就沒什么好說的了。以下代碼看起來與 SAX 代碼類似。首先,獲得 DocumentBuilderFactory (與 SAX 中的方式相同)。然后,配置類廠來處理確認和名稱空間(與 SAX 中的方式相同)。下一步,從類廠中檢索 DocumentBuilder (它與 SAXParser 類似)(與 SAX 中的方式相同. . . 啊,您都知道了)。然后,就可以進行語法分析了,產生的 DOM Document 對象傳遞給打印 DOM 樹的方法。


    清單 3. 使用文檔構建器類廠
    												
    														import java.io.File;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.io.Writer;
    
    // JAXP
    import javax.xml.parsers.FactoryConfigurationError;
    import javax.xml.parsers.ParserConfigurationException;
    import javax.xml.parsers.DocumentBuilderFactory;
    import javax.xml.parsers.DocumentBuilder;
    
    // DOM
    import org.w3c.dom.Document;
    import org.w3c.dom.DocumentType;
    import org.w3c.dom.NamedNodeMap;
    import org.w3c.dom.Node;
    import org.w3c.dom.NodeList;
    
    public class TestDOMParsing {
    
     public static void main(String[] args) {
     try {
     if (args.length != 1) {
     System.err.println ("Usage: java TestDOMParsing [filename]");
     System.exit (1);
     }
    
     // 獲得 Document Builder Factory
     DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
    
     //打開確認選項,關掉名稱空間敏感性選項。
     factory.setValidating(true);
     factory.setNamespaceAware(false);
    
     DocumentBuilder builder = factory.newDocumentBuilder();
     Document doc = builder.parse(new File(args[0]));
    
     // 從DOM 數中打印文檔,并加一初始空格
     printNode(doc, "");
    
     // 在這里也可以對 DOM 文檔進行修改
     } catch (ParserConfigurationException e) {
     System.out.println("The underlying parser does not support the requested features.");
     } catch (FactoryConfigurationError e) {
     System.out.println("Error occurred obtaining Document Builder Factory.");
     } catch (Exception e) {
     e.printStackTrace();
     }
     }
    
     private static void printNode(Node node, String indent) {
     // 打印 DOM 樹
    }
    
    												
    										

    此代碼中可能會出現兩個不同的問題(與 JAXP 中的 SAX 類似): FactoryConfigurationErrorParserConfigurationException 。每一個的原因與 SAX 中的相同。不是實現類 ( FactoryConfigurationError ) 中有問題,就是語法分析器不支持請求的特性 ( ParserConfigurationException )。DOM 和 SAX 的唯一差異是:在 DOM 中,用 DocumentBuilderFactory 替代 SAXParserFactory ,用 DocumentBuilder 替代 SAXParser 。就這么簡單!(可以 查看完整代碼清單 ,該清單包括用于打印 DOM 樹的方法。)

    使用 DOM 語法分析器

    有了 DOM 類廠之后,就可以獲得 DocumentBuilder 實例。 DocumentBuilder 實例可以使用的方法與 SAX 的非常類似。主要差異是 parse() 的變種不需要 HandlerBase 類的實例。它們返回表示語法分析之后的 XML 文檔的 DOM Document 實例。另一唯一不同之處是:為類似于 SAX 的功能提供了兩個方法:用 SAX ErrorHandler 實現來處理語法分析時可能出現的問題的 setErrorHandler() ,和用 SAX EntityResolver 實現來處理實體解析的 setEntityResolver() 。如果不熟悉這些概念,則需要通過聯機或在我的書中學習 SAX。以下清單顯示使用這些方法的示例。


    清單 4. 使用 JAXP DocumentBuilder
    												
    														    //獲得一個 DocumentBuilder 實例
     DocumentBuilder builder = builderFactory.newDocumentBuilder();
    
     //查看是否支持 Validate 選項
     boolean isValidating = builder.isValidating();
    
      //查看是否支持 namespace 選項
     boolean isNamespaceAware = builder.isNamespaceAware();
    
     // 設置一個 SAX ErrorHandler
     builder.setErrorHandler(myErrorHandlerImpl);
    
     // 設置一個 SAX EntityResolver
     builder.setEntityResolver(myEntityResolverImpl);
    
     // 運用多種方法對 file 進行語法分析
     Document doc = builder.parse(new File(args[0]));
    
     // 運用 SAX InputSource 
     Document doc = builder.parse(mySaxInputSource);
    
     // 運用 InputStream
     Document doc = builder.parse(myInputStream, myHandlerBaseInstance);
    
     // 運用 URI
     Document doc = builder.parse("http://www.newInstance.com/xml/doc.xml");
    
    												
    										

    是不是感到 DOM 這一節有些令人厭煩?有這種想法的不止您一個,寫 DOM 代碼有些令人厭煩是因為它是直接取得所學的 SAX 知識,然后將其用于 DOM。因此,和朋友、同事打賭吧,說使用 JAXP 只是小菜一碟。





    回頁首


    更改語法分析器

    最后要探討的主題是 JAXP 輕易更改類廠類使用的語法分析器的能力。更改 JAXP 使用的語法分析器實際意味著更改 類廠,因為所有 SAXParserDocumentBuilder 實例都來自這些類廠。既然確定裝入哪個語法分析器的是類廠,因此,必須更改類廠。可以通過設置 Java 系統屬性 javax.xml.parsers.SAXParserFactory 來更改要使用的 SAXParserFactory 接口實現。如果沒有定義該屬性,則返回缺省實現(供應商指定的任何語法分析器)。相同原理適用于 DocumentBuilderFactory 實現。在這種情況下,將查詢 javax.xml.parsers.DocumentBuilderFactory 系統屬性。就這么簡單,我們已經學完了!這就是 SAXP 的全部:提供到 SAX 的掛鉤,提供到 DOM 的掛鉤,并允許輕易更改語法分析器。





    回頁首


    結束語

    如您所見,沒多少復雜的東西。更改系統屬性,通過類廠、而不是語法分析器或構建器來設置“確認”,以及弄清楚JAXP實際上不是人們通常所認為的那樣,這些是使用 JAXP 的最困難部分。除了沒有 SAX 2.0 和 DOM第二層規范支持之外,JAXP 在兩個流行的 Java 和 XML API 之上提供一個有幫助的可插入層。它使代碼獨立于供應商,并允許不編譯語法分析代碼而更改語法分析器。那么,從 Sun、Apache XML 或其它方便之處下載 JAXP,并使用它吧!繼續關注 JAXP 1.1,并增加對 SAX 2 和 DOM 2、XSLT 及更多內容的支持。您將在這里獲得第一手新聞,所以,請關注 developerWorks

    posted @ 2006-11-25 17:27 tory 閱讀(298) | 評論 (0)編輯 收藏

    僅列出標題  
    主站蜘蛛池模板: 中文字幕免费在线播放| 亚洲AV无码成人专区| 亚洲中文字幕无码一久久区| 亚洲成av人片一区二区三区| 亚洲AⅤ无码一区二区三区在线 | 一个人晚上在线观看的免费视频| 色多多免费视频观看区一区| 美女视频黄a视频全免费网站一区| 国产天堂亚洲国产碰碰| 男女猛烈无遮掩视频免费软件| 四虎国产精品永免费| caoporn成人免费公开| 丝瓜app免费下载网址进入ios| 亚洲免费观看视频| 91成人在线免费视频| 国产成人免费网站| 麻豆国产人免费人成免费视频 | A在线观看免费网站大全| 久久久久久久久免费看无码| 在线免费观看视频你懂的| 国产美女无遮挡免费视频网站| 亚洲精品成人在线| 亚洲国产精品无码一线岛国| 亚洲欧洲日产韩国在线| 亚洲国产欧洲综合997久久| 色网站在线免费观看| 免费无码H肉动漫在线观看麻豆| 精品国产麻豆免费人成网站| 国产成人免费高清激情明星| 日韩成全视频观看免费观看高清| 亚洲视频在线免费| 91天堂素人精品系列全集亚洲| 亚洲中文字幕乱码熟女在线| 青青免费在线视频| 国产精品99久久免费观看| 国产1024精品视频专区免费| 免费一级做a爰片性色毛片| 亚洲av永久无码精品漫画| 亚洲精品国产精品国自产网站| 爱情岛论坛免费视频| 一级毛片免费观看不卡的|