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

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

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

    tory320

      BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
      10 隨筆 :: 0 文章 :: 1 評(píng)論 :: 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 閱讀(137) | 評(píng)論 (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("新建目錄操作 成功執(zhí)行");
    ??? }
    ??? catch(Exception e)
    ??? {
    ????? System.out.println("新建目錄操作出錯(cuò)");
    ????? 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("新建文件操作 成功執(zhí)行");
    ??? }
    ??? catch (Exception e) {
    ????? System.out.println("新建目錄操作出錯(cuò)");
    ????? e.printStackTrace();
    ?
    ??? }
    ?
    ? }
    ?
    ? /**
    ?? * 刪除文件
    ?? */
    ? public void delFile(String filePathAndName) {
    ??? try {
    ????? String filePath = filePathAndName;
    ????? filePath = filePath.toString();
    ????? File myDelFile = new File(filePath);
    ????? myDelFile.delete();
    ????? System.out.println("刪除文件操作 成功執(zhí)行");
    ??? }
    ??? catch (Exception e) {
    ????? System.out.println("刪除文件操作出錯(cuò)");
    ????? e.printStackTrace();
    ?
    ??? }
    ?
    ? }
    ?
    ? /**
    ?? * 刪除文件夾
    ?? */
    ? public void delFolder(String folderPath)
    ? {
    ??? try
    ??? {
    ????? delAllFile(folderPath); //刪除完里面所有內(nèi)容
    ????? String filePath = folderPath;
    ????? filePath = filePath.toString();
    ????? File myFilePath = new File(filePath);
    ????? myFilePath.delete(); //刪除空文件夾
    ????? System.out.println("刪除文件夾操作 成功執(zhí)行");
    ??? }
    ??? catch (Exception e)
    ??? {
    ????? System.out.println("刪除文件夾操作出錯(cuò)");
    ????? 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("刪除文件操作 成功執(zhí)行");?
    ? }
    ?
    ? /**
    ?? * 復(fù)制單個(gè)文件
    ?? * @param oldPath String 原文件路徑 如:c:/fqf.txt
    ?? * @param newPath String 復(fù)制后路徑 如: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())
    ????? { //文件存在時(shí)
    ??????? 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; //字節(jié)數(shù) 文件大小
    ????????? System.out.println(bytesum);
    ????????? fs.write(buffer, 0, byteread);
    ??????? }
    ??????? inStream.close();
    ????? }
    ??????????? System.out.println("刪除文件夾操作 成功執(zhí)行");?
    ??? }
    ??? catch (Exception e) {
    ????? System.out.println("復(fù)制單個(gè)文件操作出錯(cuò)");
    ????? e.printStackTrace();
    ?
    ??? }
    ?
    ? }
    ?
    ? /**
    ?? * 復(fù)制整個(gè)文件夾內(nèi)容
    ?? * @param oldPath String 原文件路徑 如:c:/fqf
    ?? * @param newPath String 復(fù)制后路徑 如: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("復(fù)制文件夾操作 成功執(zhí)行");?
    ??? }
    ??? catch (Exception e) {
    ????? System.out.println("復(fù)制整個(gè)文件夾內(nèi)容操作出錯(cuò)");
    ????? e.printStackTrace();
    ?
    ??? }
    ?
    ? }
    ?
    ? /**
    ?? * 移動(dòng)文件到指定目錄
    ?? * @param oldPath String 如:c:/fqf.txt
    ?? * @param newPath String 如:d:/fqf.txt
    ?? */
    ? public void moveFile(String oldPath, String newPath) {
    ??? copyFile(oldPath, newPath);
    ??? delFile(oldPath);
    ?
    ? }
    ?
    ? /**
    ?? * 移動(dòng)文件到指定目錄
    ?? * @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("使用此功能請(qǐng)按[1]? 功能一:新建目錄");
    ? ?System.out.println("使用此功能請(qǐng)按[2]? 功能二:新建文件");
    ? ?System.out.println("使用此功能請(qǐng)按[3]? 功能三:刪除文件");
    ? ?System.out.println("使用此功能請(qǐng)按[4]? 功能四:刪除文件夾");
    ? ?System.out.println("使用此功能請(qǐng)按[5]? 功能五:刪除文件夾里面的所有文件");
    ? ?System.out.println("使用此功能請(qǐng)按[6]? 功能六:復(fù)制文件");
    ? ?System.out.println("使用此功能請(qǐng)按[7]? 功能七:復(fù)制文件夾的所有內(nèi)容");
    ? ?System.out.println("使用此功能請(qǐng)按[8]? 功能八:移動(dòng)文件到指定目錄");
    ? ?System.out.println("使用此功能請(qǐng)按[9]? 功能九:移動(dòng)文件夾到指定目錄");
    ? ?System.out.println("使用此功能請(qǐng)按[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("你選擇了功能一? 請(qǐng)輸入目錄名");??
    ? ????? aa=Bin.readLine();
    ? ????? fo.newFolder(aa);
    ? ????? break;
    ? ???case 2:System.out.println("你選擇了功能二? 請(qǐng)輸入文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? System.out.println("請(qǐng)輸入在"+aa+"中的內(nèi)容");
    ? ????? bb=Bin.readLine();
    ? ????? fo.newFile(aa,bb);
    ? ????? break;
    ? ???case 3:System.out.println("你選擇了功能三? 請(qǐng)輸入文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? fo.delFile(aa);
    ? ????? break;
    ? ???case 4:System.out.println("你選擇了功能四? 請(qǐng)輸入文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? fo.delFolder(aa);
    ? ????? break;
    ? ???case 5:System.out.println("你選擇了功能五? 請(qǐng)輸入文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? fo.delAllFile(aa);
    ? ????? break;??
    ? ???case 6:System.out.println("你選擇了功能六? 請(qǐng)輸入文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? System.out.println("請(qǐng)輸入目標(biāo)文件名");?
    ? ????? bb=Bin.readLine();
    ? ????? fo.copyFile(aa,bb);
    ? ????? break;
    ? ???case 7:System.out.println("你選擇了功能七? 請(qǐng)輸入源文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? System.out.println("請(qǐng)輸入目標(biāo)文件名");?
    ? ????? bb=Bin.readLine();
    ? ????? fo.copyFolder(aa,bb);
    ? ????? break;? ?????
    ? ???case 8:System.out.println("你選擇了功能八? 請(qǐng)輸入源文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? System.out.println("請(qǐng)輸入目標(biāo)文件名");?
    ? ????? bb=Bin.readLine();
    ? ????? fo.moveFile(aa,bb);
    ? ????? break;
    ? ??? ?case 9:System.out.println("你選擇了功能九? 請(qǐng)輸入源文件名");??
    ? ????? aa=Bin.readLine();
    ? ????? System.out.println("請(qǐng)輸入目標(biāo)文件名");?
    ? ????? bb=Bin.readLine();
    ? ????? fo.moveFolder(aa,bb);
    ? ????? break;? ?????
    ? ???case 10:exitnow=true;
    ? ?????? System.out.println("程序結(jié)束,請(qǐng)退出");
    ? ????? break;
    ? ???default:System.out.println("輸入錯(cuò)誤.請(qǐng)輸入1-10之間的數(shù)");?? ???? ????? ?
    ? ?? }
    ? ??
    ? ??
    ? ??System.out.println("請(qǐng)重新選擇功能");
    ? ??
    ? ??
    ? ??}
    ? ??catch(Exception e)
    ? ??{
    ? ??System.out.println("輸入錯(cuò)誤字符或程序出錯(cuò)");
    ? ??}
    ? ??
    ?}? ?
    ?}
    }
    posted @ 2007-01-30 13:03 tory 閱讀(187) | 評(píng)論 (0)編輯 收藏

    2007年1月19日 #

    本文是在參閱了http://ivanl.javaeye.com/blog/24739基礎(chǔ)上完成的
    在看JPetStore的代碼時(shí),發(fā)現(xiàn)它的分頁(yè)處理主要是通過(guò)返回PaginatedList對(duì)象來(lái)完成的。如:在CatalogService類(lèi)中
    public?PaginatedList?getProductListByCategory(String?categoryId)?{?
    ????
    return?productDao.getProductListByCategory(categoryId);?
    ??}
    ?

    分頁(yè)是操作數(shù)據(jù)庫(kù)型系統(tǒng)常遇到的問(wèn)題。分頁(yè)實(shí)現(xiàn)方法很多,但效率的差異就很大了。iBatis是通過(guò)什么方式來(lái)實(shí)現(xiàn)這個(gè)分頁(yè)的了。查看它的實(shí)現(xiàn)部分:
    ?
    返回的PaginatedList實(shí)際上是個(gè)接口,實(shí)現(xiàn)這個(gè)接口的是PaginatedDataList類(lèi)的對(duì)象,查看PaginatedDataList類(lèi)發(fā)現(xiàn),每次翻頁(yè)的時(shí)候最后都會(huì)調(diào)用下面這段函數(shù)
    private?List?getList(int?idx,?int?localPageSize)?throws?SQLException?{?
    ????
    return?sqlMapExecutor.queryForList(statementName,?parameterObject,?(idx)?*?pageSize,?localPageSize);?
    ??}
    ?
    由于
    public?interface?SqlMapClient?extends?SqlMapExecutor,?SqlMapTransactionManager?{……}?

    所以實(shí)際的調(diào)用次序如下:
    SqlMapClientImpl.queryForPaginatedList->SqlMapSessionImpl.queryForPaginatedList?
    ->SqlMapExecutorDelegate.queryForPaginatedList->GeneralStatement.executeQueryForList?
    ->GeneralStatment.executeQueryWithCallback->GeneralStatment.executeQueryWithCallback?
    ->SqlExecutor.executeQuery->SqlExecutor.handleMultipleResults()->SqlExecutor.executeQuery->?handleResults?
    分頁(yè)處理的函數(shù)如下
    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);?
    ????}
    ?
    ??}
    ?

    由此可見(jiàn),iBatis的分頁(yè)主要依賴(lài)于jdbcdriver的如何實(shí)現(xiàn)以及是否支持rs.absolute(skipResults)。它并不是一個(gè)好的分頁(yè)方式。它先要取出所有的符合條件的記錄存入ResultSet對(duì)象,然后用absolute方法進(jìn)行定位,來(lái)實(shí)現(xiàn)分頁(yè)。當(dāng)記錄數(shù)較大(比如十萬(wàn)條)時(shí),整體的查詢(xún)速度將會(huì)變得很慢。
    所以分頁(yè)還是要考慮采用直接操作sql語(yǔ)句來(lái)完成。當(dāng)然小批量的可以采用iBatis的分頁(yè)模式。一般分頁(yè)的sql語(yǔ)句與數(shù)據(jù)庫(kù)的具體實(shí)現(xiàn)有關(guān)
    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分頁(yè)采用的就是是拼湊RowNum的Sql語(yǔ)句來(lái)完成的。參考代碼如下:?
    ?
    ????????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自帶的分頁(yè)類(lèi),大批量的還是直接操縱sql,當(dāng)然也可以將這些sql自己進(jìn)行封裝,或在包中封裝都可以。包封裝的示例代碼如下:
    一個(gè)封裝了分頁(yè)功能的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 閱讀(830) | 評(píng)論 (0)編輯 收藏

    2006年12月29日 #

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

      JFreeChart是開(kāi)放源代碼站點(diǎn)SourceForge.net上的一個(gè)JAVA項(xiàng)目。它的功能十分強(qiáng)大,能創(chuàng)建餅圖、柱狀圖(普通柱狀圖以及堆棧柱狀圖)、線圖、區(qū)域圖、分布圖、混合圖、甘特圖以及一些儀表盤(pán)等等,并可生成PNG或JPG圖片格式文件。
      本人在學(xué)習(xí)過(guò)程中發(fā)現(xiàn),網(wǎng)上很多文章都是講一些JFreeChart的基本應(yīng)用,而對(duì)JFreeChart生成熱點(diǎn)圖表這樣常用的功能雖有所提及卻沒(méi)有一個(gè)完整的例子,所以我就寫(xiě)一個(gè)簡(jiǎn)單示例供大家參考,希望對(duì)大家的學(xué)習(xí)有所幫助。?

      <二>示例說(shuō)明:

      假設(shè)有一個(gè)關(guān)于程序員北京,上海,廣洲三地程序員學(xué)歷,開(kāi)發(fā)語(yǔ)言,薪金情況的調(diào)查。首先要以餅圖顯示程序員學(xué)歷的分布情況(index.jsp)。點(diǎn)擊餅圖的每一部分會(huì)以柱狀圖顯示該層次程序員所用開(kāi)發(fā)語(yǔ)言和薪金的情況(barview.jsp)。重點(diǎn)演示怎樣在餅圖上添加鏈接。?

      <三>準(zhǔn)備工作:

      1.下載最新版本的JFreeChart,當(dāng)前為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復(fù)制到WEB應(yīng)用的lib目錄下。

      3.在web.xml文件中增加以下內(nèi)容:

    <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>?

      <四>餅圖頁(yè)面代碼(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("大專(zhuān)",5700);?
    data.setValue("本科",8280);?
    data.setValue("碩士",4420);?
    data.setValue("博士",80);?

    PiePlot3D?plot?=?new?PiePlot3D(data);//3D餅圖?
    plot.setURLGenerator(new?StandardPieURLGenerator("barview.jsp"));//設(shè)定鏈接?
    JFreeChart?chart?=?new?JFreeChart("",JFreeChart.DEFAULT_TITLE_FONT,?plot,?true);?
    chart.setBackgroundPaint(java.awt.Color.white);//可選,設(shè)置圖片背景色?
    chart.setTitle("程序員學(xué)歷情況調(diào)查表");//可選,設(shè)置圖片標(biāo)題?
    plot.setToolTipGenerator(new?StandardPieItemLabelGenerator());?
    StandardEntityCollection?sec?=?new?StandardEntityCollection();?
    ChartRenderingInfo?info?=?new?ChartRenderingInfo(sec);?
    PrintWriter?w?=?new?PrintWriter(out);//輸出MAP信息?
    //500是圖片長(zhǎng)度,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>?

      生成的圖片如下


      在瀏覽器中點(diǎn)右鍵->查看源文件會(huì)發(fā)現(xiàn)有以下一段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="大專(zhuān)?=?5,700"?alt=""?href="barview.jsp?category=大專(zhuān)&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信息,我們?cè)贗MG標(biāo)簽中加入usemap="#map0"就可以為餅圖的每一部分加入鏈接。

      <五>柱狀圖頁(yè)面代碼:(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("大專(zhuān)"))?
    {?
     dataset=getDataSet();?
    }?
    else?if(category.equals("碩士")||category.equals("博士"))?
    {?
     dataset=getDataSet2();?
    }else?
    {?
     dataset=getDataSet3();?
    }?
    String?title=category+"程序員在各城市薪金情況統(tǒng)計(jì)";?
    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 閱讀(388) | 評(píng)論 (0)編輯 收藏

    2006年12月25日 #

    ?
    在用 AJAX 開(kāi)發(fā)的過(guò)程中, 不可避免的會(huì)遇到中文問(wèn)題. 很多原來(lái)可以通過(guò)表單進(jìn)行 POST 提交的字符, 到了用 AJAX 實(shí)現(xiàn)的時(shí)候, 就會(huì)出現(xiàn)煩人的亂碼和丟特殊字符的現(xiàn)象. 另外服務(wù)器端返回值如何解析, 也是一個(gè)很煩人的問(wèn)題. 本文將就個(gè)人的一點(diǎn)實(shí)踐經(jīng)驗(yàn)作出總結(jié), 并給出一個(gè)盡量簡(jiǎn)單可行, 復(fù)用性高的方案. 目的不是替代你喜歡的 AJAX 框架, 而是希望幫助您理解和處理可能遇到的問(wèn)題.

    開(kāi)始之前: 首先一個(gè)問(wèn)題就是通常 XMLHttpRequest 默認(rèn)的編碼都是UTF-8的, 所以我們建議所有頁(yè)面, 客戶(hù)端和服務(wù)器端都使用 UTF-8 作為編碼.

    1. base64 encode 和 decode
    ??? 這個(gè)方案依賴(lài)于 JavaScript 實(shí)現(xiàn)的 base64 編碼/解碼方法, 在客戶(hù)端發(fā)送參數(shù)的時(shí)候用 base64 進(jìn)行編碼, 服務(wù)器端通過(guò) base64 進(jìn)行解碼后還原出原來(lái)的字符, 這個(gè)解決方案可以滿(mǎn)足需要, 但是有個(gè)問(wèn)題就是一是增加了客戶(hù)端代碼量, 還有個(gè)大問(wèn)題就是編碼后的內(nèi)容比原始內(nèi)容會(huì)大很多, 另外如果找到的 base64 JS 算法不夠標(biāo)準(zhǔn)的話, 服務(wù)器端就無(wú)法還原原來(lái)的值了. 現(xiàn)在網(wǎng)上有很多種 base64 的 JS 實(shí)現(xiàn)代碼, 例如如下的一個(gè)算法實(shí)現(xiàn):

    <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="轉(zhuǎn)換" ONCLICK="doit()">
    </FORM>
    </BODY>

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

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

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

    escape() & encodeURI() & encodeURIComponent()這三個(gè)函數(shù)都可以用來(lái)對(duì)URI進(jìn)行encode或過(guò)濾特殊字符(#/$&+=?/等)。我的經(jīng)驗(yàn)是最好用encodeURIComponent()(需要IE 5.5以上,F(xiàn)ireFox當(dāng)然沒(méi)問(wèn)題),因?yàn)閷?duì)UTF-8支持比較好,不會(huì)遇到中文亂碼問(wèn)題,否則還需要進(jìn)行編碼轉(zhuǎn)換,很麻煩的。使用其它兩個(gè)函數(shù)都會(huì)發(fā)生丟失特殊字符的問(wèn)題,例如空格變+號(hào)或者空格,引號(hào),&=?等丟失的問(wèn)題, 至少使用 JSP 作為服務(wù)器端的話會(huì)發(fā)生這種情況, 有興趣的朋友可以將本文最后的例子代碼中的編碼部分修改后做個(gè)測(cè)試.

    下面是MSDN上對(duì)這三個(gè)函數(shù)的解釋?zhuān)?/p>

    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. 使用一個(gè)簡(jiǎn)便的客戶(hù)端數(shù)據(jù)解析方案

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

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

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

    好了, 解析出來(lái)了!
    如果要傳遞多個(gè)變量呢, 就用 var _dataObject1, var _dataObject2...這樣就可以了, 客戶(hù)端就依次是 _dataObject1.username, _dataObject2.username...

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

    ??? 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();
    ??? }

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

    4.?實(shí)例代碼
    好了, 說(shuō)了這么多, 就推出個(gè)人的解決方案吧. 簡(jiǎn)單的講就是我寫(xiě)了一個(gè)腳本對(duì)象 AjaxFormer, 使用的是 escape來(lái)自動(dòng)的將原來(lái)的 POST/GET 方式的提交代碼自動(dòng)的轉(zhuǎn)換成 AJAX 的方式.

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

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

    用法示例:
    <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>

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

    運(yùn)行時(shí)的效果如圖所示:
    http://m.tkk7.com/images/blogjava_net/beansoft/18680/o_ajaxFormer.png

    下載本文的源碼:?

    AJAXFormer.zip4KB

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

    作者: beansoft@126.com?2006.12.25

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

    2006年12月24日 #

    ? 在這篇 ASP.NET 中Session 實(shí)現(xiàn)原理淺析[2] 狀態(tài)管理器 Blog中, Flier 老大向大家推薦了Java中幾種Cache的實(shí)現(xiàn),于是我就按圖索驥,首先是找到OSCache的老家: OpenSymphony 。哇,這里的資源真多啊,簡(jiǎn)直就是一個(gè)寶藏。為什么原來(lái)就沒(méi)有發(fā)現(xiàn)呢?感謝Flier老大了!
    ??????? 在OpenSymphony的主頁(yè)上,我看到了兩個(gè)讓我覺(jué)得很親切的項(xiàng)目:WebWork和Quarts。WebWork作為一個(gè)實(shí)現(xiàn)了IOC的輕量級(jí)Web Application Framework一直備受開(kāi)發(fā)者的青睞;而Quarts更是在異步信息處理上大展拳腳了。看到了老朋友,我就只好暫時(shí)將新歡OSCache擺一邊,找出我很久沒(méi)有聯(lián)系的老貓(Tomcat)去跟WebWork聊嗑了。
    ??????? 做好了一切的準(zhǔn)備工作之后[1],我嘗試著做一個(gè)登陸注冊(cè)的功能,以體驗(yàn)一下WebWork。跟其他的Web應(yīng)用一樣,我們首先要建立一個(gè)標(biāo)準(zhǔn)的WEB-INF目錄(所謂標(biāo)準(zhǔn)就是目錄下面包含lib和classes子目錄以及Web.xml文件),接著在WEB-INF/lib下面放上WebWork所需要的.jar文件并在WEB-INF目錄下建立Web.xml文件[2]。在完成了這些千篇一律的工作之后,我仔細(xì)的看了一下Web.xml中內(nèi)容,它里面只定義了一個(gè)servlet:webwork,其對(duì)應(yīng)的class為com.opensymphony.webwork.dispatcher.ServletDispatcher,然后由webwork這個(gè)servlet去處理所有.action的請(qǐng)求。看到這里,我暗自竊喜,這跟我熟悉的WAF框架是十分類(lèi)似的,只不過(guò)MainServlet變成了ServletDispatcher,.do的請(qǐng)求變成了.action而已。
    ??????? 接下來(lái),就是到classes目錄下建立xwork.xml文件,這個(gè)文件跟WAF中的mappings.xml很相似,因?yàn)閷?duì).action的處理都是在這里被定義的,而WAF中關(guān)于.do的處理則定義在mapping.xml中。但是也有一些我并不清楚的東西,如package和Interceptor。在classes目錄下還要建立一個(gè)validator.xml文件,但是這個(gè)并不是必需的。做好了這些準(zhǔn)備工作之后,就真正開(kāi)始WebWork的體驗(yàn)之旅了。
    ?????? ?1、建立一個(gè)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屬性的寫(xiě)法了;
    ??????? 2、在xwork.xml中增加相應(yīng)的處理action的節(jié)點(diǎn)

    ?<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節(jié)點(diǎn)中name的值要與index.jsp中定義的action的名字要嚴(yán)格匹配,對(duì)于大小 寫(xiě)是敏感的。在action節(jié)點(diǎn)中還包含了節(jié)點(diǎn),以說(shuō)明處理action之后會(huì)可能出現(xiàn)的不同結(jié)果(name)和相應(yīng)的處理方式(type)。譬如說(shuō),如果Login的這個(gè)action處理success了,則使用dispatcher將結(jié)果分(dispatch)到相應(yīng)的頁(yè)面。而在WAF框架中并沒(méi)有這樣的定義,因?yàn)樵谀J(rèn)情況下如果不成功則會(huì)返回當(dāng)前頁(yè)面,不過(guò)可以定義FlowHandler,并擁有類(lèi)似的功能且更加靈活;
    ??????? 3、實(shí)現(xiàn)類(lèi)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;
    ????}

    }

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

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

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

    2006年12月21日 #

         摘要: 一 Ajax-- 整合的力量 2005 年,伴隨著 Web2.0 的東風(fēng), Ajax 逐漸進(jìn)入國(guó)內(nèi)開(kāi)發(fā)人...  閱讀全文
    posted @ 2006-12-21 22:40 tory 閱讀(296) | 評(píng)論 (0)編輯 收藏

    2006年12月18日 #

    重載overloading和覆寫(xiě)overriding哪個(gè)更早執(zhí)行-- visitor幫助篇

    重載overloading和覆寫(xiě)overriding哪個(gè)更早執(zhí)行--?? visitor幫助篇
    一:?jiǎn)栴}提出
    雖然我們經(jīng)常寫(xiě)程序用到重載和覆寫(xiě),但是很少會(huì)考慮他們的執(zhí)行順序。下邊的內(nèi)容就是關(guān)于,他們同時(shí)出現(xiàn)時(shí)
    哪個(gè)先起作用:
    二:?jiǎn)栴}分析
    Java是"動(dòng)態(tài)單分派靜態(tài)多分派語(yǔ)言",這個(gè)定義已經(jīng)多次提起,如果你不了解這些概念,看這里"visitor模式準(zhǔn)備"
    所以就注定了重載(靜態(tài)多分派)要早于覆寫(xiě)(動(dòng)態(tài)單分派),因?yàn)殪o態(tài)分派是編繹期實(shí)現(xiàn)的,動(dòng)態(tài)分派是執(zhí)行期實(shí)現(xiàn)的。
    三:驗(yàn)證
    簡(jiǎn)單驗(yàn)證一下,順變提高記憶

    ?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 }


    運(yùn)行結(jié)果是什么?
    in Child +param:Object
    inChild是確認(rèn)的,但是為什么是object,而不是String,我們放入的就是String啊。
    首先來(lái)分析執(zhí)行過(guò)程。
    定義韋類(lèi)型Parent p在執(zhí)行run(Str)的時(shí)候,

    1,如果是先執(zhí)行重載,然後是執(zhí)行覆寫(xiě)的過(guò)程
    重載時(shí)因?yàn)檎也坏綄?duì)應(yīng)的String參數(shù)的函數(shù),所以定位到接受父類(lèi)的run(Object o)函數(shù),
    覆寫(xiě)時(shí)因?yàn)閭魅霑r(shí)父類(lèi)告訴子類(lèi)的對(duì)象類(lèi)型是Object,所以執(zhí)行run(Object o);
    正是我們看到的結(jié)果,所以在Java中執(zhí)行的順序是這樣的。

    2,為了對(duì)比,說(shuō)一下先覆寫(xiě)后重載的過(guò)程
    如果是先覆寫(xiě),再重載
    覆寫(xiě)時(shí)因?yàn)榇_定對(duì)象實(shí)際是子類(lèi),所以直接覆寫(xiě)到Child,然後重載,發(fā)現(xiàn)有對(duì)應(yīng)的String為參數(shù)的函數(shù)
    執(zhí)行,run(String str);
    應(yīng)該輸出的結(jié)果:in Child +param:String
    但我們看到結(jié)果顯然是1,所以驗(yàn)證了Java是先重載后覆寫(xiě)的。


    最后的部分:

    看完本文,如果你對(duì)visitor模式有更多的興趣,想了解更多請(qǐng)看如下幾篇文章。
    1,靜態(tài)分派,動(dòng)態(tài)分派,多分派,單分派 --------------?? visitor模式準(zhǔn)備
    2,訪問(wèn)差異類(lèi)型的集合類(lèi) ------------------------?? visitor模式入門(mén)
    3,visitor模式理論及學(xué)術(shù)概念-------------------?? visitor模式深入
    4,visitor模式和其它模式的比較和關(guān)系-------------?? visitor模式總結(jié)?
    5,重載overloading和覆寫(xiě)overriding哪個(gè)更早執(zhí)行--?? visitor幫助篇 (本文)
    雖然排列順序是1,2,3,4,5 但是我個(gè)人建議的學(xué)習(xí)方式是2,1,3,4,5因?yàn)檫@個(gè)順序更方便一般人理解

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

    2006年12月10日 #

    所謂RCP,就是Rich Client Platform的縮寫(xiě),即富客戶(hù)平臺(tái),是Eclipse進(jìn)化的產(chǎn)物(自3.0版以后出現(xiàn)),是Eclipse組織向用戶(hù)提供的強(qiáng)大的開(kāi)放性開(kāi)發(fā)平臺(tái),能夠使用戶(hù)方便地創(chuàng)建自己的基于Eclipse的應(yīng)用程序,并且這些應(yīng)用程序能夠得到Eclipse的底層支持。更重要的是,我們可以利用Java創(chuàng)建象Eclipse這么漂亮的桌面程序。

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

      下面讓我們一步一步來(lái)建立一個(gè)Eclipse RCP程序,下面的內(nèi)容可以說(shuō)在Google上一搜一大把,有些人會(huì)覺(jué)得乏味,但是沒(méi)關(guān)系,這只是一個(gè)快速的起步。

      選擇“新建--項(xiàng)目”,選擇“插件項(xiàng)目”:
    rcp00.JPG

    rcp01.JPG

    點(diǎn)下一步,輸入項(xiàng)目名稱(chēng),選擇Eclipse版本,我這里選擇的是3.2:
    rcp02.JPG

      點(diǎn)下一步,插件標(biāo)識(shí)和插件名稱(chēng)可以更改,其他的內(nèi)容都可以保持默認(rèn),一定要記得選中富客戶(hù)機(jī)應(yīng)用程序支持:
    rcp03.JPG

      點(diǎn)下一步,選中一個(gè)模板,這里選一個(gè)最簡(jiǎn)單的,到時(shí)候看源代碼的時(shí)候便于理解:
    rcp04.JPG

      點(diǎn)下一步,改一下應(yīng)用程序標(biāo)題:
    rcp05.JPG

      點(diǎn)完成,我們可以在項(xiàng)目上面點(diǎn)右鍵,選擇按Eclipse程序運(yùn)行,就可以看到效果了:
    rcp16.JPG

    rcp17.JPG

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

      現(xiàn)在,這個(gè)程序只能在Eclipse環(huán)境下運(yùn)行,而RCP的目標(biāo)是創(chuàng)建可以獨(dú)立運(yùn)行的應(yīng)用程序,我們的事情還沒(méi)完呢。下一步,在項(xiàng)目上點(diǎn)右鍵,創(chuàng)建產(chǎn)品配置文件:
    rcp06.JPG

      輸入產(chǎn)品配置文件名:

    rcp07.JPG

      生成的產(chǎn)品配置文件在編輯器中打開(kāi),應(yīng)該是這個(gè)樣子的:
    rcp09.JPG

      剛開(kāi)始,上面的幾個(gè)文本框都是空的,點(diǎn)新建按鈕之后,彈出如下的對(duì)話框,輸入產(chǎn)品名稱(chēng)后,點(diǎn)完成就行了。

    rcp08.JPG

      點(diǎn)擊配置文件中的“啟動(dòng)程序”,我們可以試著啟動(dòng)我們的RCP程序。結(jié)果呢,會(huì)出錯(cuò)。原因很簡(jiǎn)單,因?yàn)槲覀儧](méi)有為我們的程序選中它依賴(lài)的插件。

    ?  選中配置文件的“配置”選項(xiàng)卡,添加以下幾個(gè)依賴(lài)項(xiàng),記住,一定要把我們自己,也就是com.blogjava.youxia.rcp_start加進(jìn)依賴(lài)項(xiàng),否則會(huì)出錯(cuò)。最開(kāi)始的時(shí)候,就是這么一點(diǎn)小問(wèn)題,讓我浪費(fèi)了幾天時(shí)間。
    rcp10.JPG

      再點(diǎn)擊添加必須的插件,自動(dòng)添加其它的依賴(lài)項(xiàng)。

      再下一步,設(shè)置項(xiàng)目的構(gòu)建路徑,如下圖:
    rcp11.JPG

      下一步,導(dǎo)出我們的程序:
    rcp12.JPG

    rcp13.JPG

      點(diǎn)下一步,輸入我們程序?qū)С龅哪夸洠缦聢D:
    rcp14.JPG

      點(diǎn)完成按鈕之后,我們的程序就導(dǎo)出到我們的指定的目錄中了,打開(kāi)這個(gè)目錄,可以看到一個(gè)類(lèi)似eclipse的程序圖標(biāo),雙擊運(yùn)行,效果如下圖:rcp15.JPG

      最后,需要說(shuō)明兩點(diǎn):第一,如果希望生成的程序有自己的圖標(biāo),可以在產(chǎn)品配置文件中的最后兩個(gè)配置文件中設(shè)置;第二,生成的程序應(yīng)該是沒(méi)有菜單欄的,因?yàn)槲业腅clipse安裝了MyEclipse,所以導(dǎo)出的程序就多了兩個(gè)菜單。

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

    2006年11月25日 #

    JAXP 專(zhuān)述

    Sun 的 Java API for XML 語(yǔ)法分析

    ?

    ?

    未顯示需要 JavaScript 的文檔選項(xiàng)


    ?


    級(jí)別: 初級(jí)

    Brett McLaughlin , Enhydra 策略顧問(wèn), Lutris Technologies

    2000 年 11 月 01 日

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

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

    什么是 JAXP?

    本文假設(shè)您有 SAX 和 DOM 的基本知識(shí)。這里實(shí)在沒(méi)有足夠篇幅來(lái)解釋 SAX、DOM 和 JAXP。如果您是 XML 語(yǔ)法分析的新手,那么可能要通過(guò)聯(lián)機(jī)資源閱讀 SAX 和 DOM,或者瀏覽我的書(shū)。( 參考資源 一節(jié)中有至 API 和我的書(shū)的鏈接。)獲得基本知識(shí)后再看本文會(huì)比較好。





    回頁(yè)首


    API 還是抽象?

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

    雖然要分別講述所有這些特性,但是真正需要掌握的是:JAXP 不提供語(yǔ)法分析功能 !沒(méi)有 SAX、DOM 或另一個(gè) XML 語(yǔ)法分析 API,就 無(wú)法分析 XML 語(yǔ)法 。有很多人曾讓我將 DOM、SAX 或 JDOM 與 JAXP 進(jìn)行對(duì)比。但進(jìn)行這些對(duì)比是不可能的,因?yàn)榍叭齻€(gè) API 與 JAXP 的目的完全不同。SAX、DOM 和 JDOM 都分析 XML 語(yǔ)法。而 JAXP 卻提供到達(dá)這些語(yǔ)法分析器和結(jié)果的方式。它自身不提供分析文檔語(yǔ)法的新方法。如果要正確使用 JAXP,則一定要弄清這點(diǎn)。這將使您比其它 XML 開(kāi)發(fā)人員領(lǐng)先一大截。

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





    回頁(yè)首


    Sun 的 JAXP 和 Sun 的語(yǔ)法分析器

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

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





    回頁(yè)首


    舊和新

    關(guān)于 JAXP,最后需要指出的是:使用 JAXP 有一些缺陷。例如,JAXP 只支持 SAX 1.0 和 DOM 第一層規(guī)范。SAX 2.0 從 2000 年 5 月起就完成,DOM 第二層規(guī)范支持甚至在大多數(shù)語(yǔ)法分析器中存在更長(zhǎng)時(shí)間。DOM 第二層規(guī)范還沒(méi)有完成,但確實(shí)足夠穩(wěn)定以用于生產(chǎn)。這兩個(gè) API 的新版本都有重大改進(jìn),最明顯的是對(duì) XML 名稱(chēng)空間的支持。該支持還允許“XML Schema 確認(rèn)”,這個(gè)與 XML 相關(guān)的另一熱門(mén)技術(shù)。公平地說(shuō),當(dāng) JAXP 發(fā)布 1.0 最終發(fā)行版時(shí),SAX 2.0 和 DOM 第一層規(guī)范都還沒(méi)有完成。但是,由于沒(méi)有包括這些新版本,確實(shí)為開(kāi)發(fā)人員帶來(lái)很大不便。

    還可以使用 JAXP,但是也可以等待 JAXP 1.1,它支持 SAX 2.0 和 DOM第二層規(guī)范 。否則,將發(fā)現(xiàn),JAXP 提供的優(yōu)點(diǎn)以 SAX 和 DOM 最新版本中的功能為代價(jià),并使應(yīng)用程序更加難以編碼。無(wú)論是否等待下一個(gè) JAXP 發(fā)行版,都要留意這個(gè)問(wèn)題。如果將 JAXP 與語(yǔ)法分析器一起使用,而語(yǔ)法分析器支持的 DOM 和 SAX 版本比 JAXP 支持的要高,則可能會(huì)有類(lèi)路徑問(wèn)題。所以,事先留意一下,并且,一旦有 JAXP 1.1,馬上升級(jí)。基本理解 JAXP 之后,讓我們看一下 JAXP 依賴(lài)的 API:SAX 和 DOM。





    回頁(yè)首


    從 SAX 開(kāi)始

    SAX (Simple API for XML)是用于處理 XML 的事件驅(qū)動(dòng)方法。它基本由許多回調(diào)函數(shù)組成。例如,每當(dāng) SAX 語(yǔ)法分析器遇到元素的開(kāi)始標(biāo)記時(shí)就調(diào)用 startElement() 。對(duì)于字符串,將調(diào)用 characters() 回調(diào)函數(shù),然后在元素結(jié)束標(biāo)記處調(diào)用 endElement() 。還有很多回調(diào)函數(shù)用于文檔處理、錯(cuò)誤和其它詞匯結(jié)構(gòu)。現(xiàn)在知道這是怎么回事了。SAX 程序員實(shí)現(xiàn)一個(gè)定義這些回調(diào)函數(shù)的 SAX 接口。SAX 還實(shí)現(xiàn)一個(gè)名為 HandlerBase 的類(lèi),該類(lèi)實(shí)現(xiàn)所有這些回調(diào)函數(shù),并提供所有這些回調(diào)方法的缺省空實(shí)現(xiàn)。(提到這一點(diǎn)是因?yàn)樗诤竺嬷v到的 DOM 中很重要。)SAX 開(kāi)發(fā)人員只需擴(kuò)展這個(gè)類(lèi),然后實(shí)現(xiàn)需要插入特定邏輯的方法。所以,SAX 的關(guān)鍵在于為這些不同的回調(diào)函數(shù)提供代碼,然后允許語(yǔ)法分析器在適當(dāng)?shù)臅r(shí)候觸發(fā)這些回調(diào)函數(shù)中的每一個(gè)。

    因此,典型的 SAX 過(guò)程如下:

    • 用特定供應(yīng)商的語(yǔ)法分析器實(shí)現(xiàn)創(chuàng)建一個(gè) SAXParser 實(shí)例
    • 注冊(cè)回調(diào)實(shí)現(xiàn)(例如,通過(guò)使用擴(kuò)展 HandlerBase 的類(lèi))
    • 開(kāi)始進(jìn)行語(yǔ)法分析,然后在觸發(fā)回調(diào)實(shí)現(xiàn)時(shí)等待

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

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

    SAX 語(yǔ)法分析器一瞥

    JAXP SAXParserFactory 類(lèi)是能夠輕易更改語(yǔ)法分析器實(shí)現(xiàn)的關(guān)鍵所在。必須創(chuàng)建這個(gè)類(lèi)的新實(shí)例(等一會(huì)將講到)。創(chuàng)建新實(shí)例之后,類(lèi)廠提供一個(gè)方法來(lái)獲得支持 SAX 的語(yǔ)法分析器。在內(nèi)部,JAXP 實(shí)現(xiàn)處理依賴(lài)于供應(yīng)商的代碼,使您的代碼不受影響。這個(gè)類(lèi)廠還提供其它一些優(yōu)秀特性。

    除創(chuàng)建 SAX 語(yǔ)法分析器實(shí)例的基本工作之外,類(lèi)廠還允許設(shè)置配置選項(xiàng)。這些選項(xiàng)影響所有通過(guò)類(lèi)廠獲得的語(yǔ)法分析器實(shí)例。JAXP 1.0 中兩個(gè)可用的功能是設(shè)置名稱(chēng)空間敏感性 ( setNamespaceAware (boolean awareness)),和打開(kāi)確認(rèn) ( setValidating (boolean validating))。請(qǐng)記住,一旦設(shè)置了這些選項(xiàng),在調(diào)用該方法之后,它們將影響 所有從 類(lèi)廠獲得的實(shí)例。

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


    清單 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 語(yǔ)法分析器類(lèi)廠
     SAXParserFactory factory = SAXParserFactory.newInstance();
    
     //設(shè)置設(shè)置名稱(chēng)空間敏感性選項(xiàng),關(guān)掉確認(rèn)選項(xiàng)
     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 {
     //通過(guò) DocumentHandler, ErrorHandler等實(shí)現(xiàn)的SAX回調(diào)函數(shù)
    }
    
    												
    										

    請(qǐng)注意,在這段代碼中,在使用類(lèi)廠時(shí)可能發(fā)生兩個(gè)特定于 JAXP 的問(wèn)題:無(wú)法獲得或配置 SAX 類(lèi)廠,以及無(wú)法配置 SAX 語(yǔ)法分析器。當(dāng)無(wú)法獲得 JAXP 實(shí)現(xiàn)中指定的語(yǔ)法分析器或系統(tǒng)屬性時(shí),通常會(huì)發(fā)生第一個(gè)問(wèn)題 FactoryConfigurationError 。當(dāng)正在使用的語(yǔ)法分析器中的特性不可用時(shí),會(huì)發(fā)生第二個(gè)問(wèn)題 ParserConfigurationException 。這兩個(gè)問(wèn)題都容易處理,應(yīng)該不會(huì)對(duì) JAXP 的使用造成任何困難。

    在獲得類(lèi)廠、關(guān)閉名稱(chēng)空間并打開(kāi)“確認(rèn)”之后,將獲得 SAXParser ,然后開(kāi)始語(yǔ)法分析。請(qǐng)注意, SAX 語(yǔ)法分析器的 parse() 方法取得前面提到的 SAX HandlerBase 類(lèi)的一個(gè)實(shí)例。(可以通過(guò)完整的 Java 清單 查看該類(lèi)的實(shí)現(xiàn) 。)還要傳遞要進(jìn)行語(yǔ)法分析的文件。但是, SAXParser 所包含的遠(yuǎn)不止這一個(gè)方法。

    使用 SAX 語(yǔ)法分析器

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

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

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


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

    目前為止,關(guān)于 SAX 已經(jīng)講了很多,但是還沒(méi)有揭示任何不尋常或令人驚奇的東西。事實(shí)上,JAXP 的功能很少,特別是當(dāng) SAX 也牽涉進(jìn)來(lái)時(shí)。這很好,因?yàn)橛凶钌俚墓δ苄砸馕吨a可移植性更強(qiáng),并可以由其他開(kāi)發(fā)人員與任何與 SAX 兼容的 XML 語(yǔ)法分析器一起使用,無(wú)論是免費(fèi)(通過(guò)開(kāi)放源碼,希望如此)還是通過(guò)商業(yè)途徑。就是這樣。在 JAXP 中使用 SAX 沒(méi)有更多的東西。如果已經(jīng)知道 SAX,那么現(xiàn)在已經(jīng)掌握大約 98% 的內(nèi)容。只需學(xué)習(xí)兩個(gè)新類(lèi)和兩個(gè) Java 異常,您就可以開(kāi)始了。如果從沒(méi)使用過(guò) SAX,那也很簡(jiǎn)單,現(xiàn)在就可以開(kāi)始。





    回頁(yè)首


    處理 DOM

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

    DOM 和 SAX 的主要差異是它們的 API 結(jié)構(gòu)。SAX 包含一個(gè)基于事件的回調(diào)函數(shù)集,而 DOM 有一個(gè)內(nèi)存中的樹(shù)狀結(jié)構(gòu)。換句話說(shuō),在 SAX 中,從不需要操作數(shù)據(jù)結(jié)構(gòu)(除非開(kāi)發(fā)人員手工創(chuàng)建)。因此,SAX 不提供修改 XML 文檔的功能。而 DOM 正好提供這種類(lèi)型的功能。 org.w3c.dom.Document 類(lèi)表示 XML 文檔,它由表示元素、屬性和其它 XML 結(jié)構(gòu)的 DOM 節(jié)點(diǎn) 組成。所以,JAXP 無(wú)需觸發(fā) SAX 回調(diào),它只負(fù)責(zé)從語(yǔ)法分析返回一個(gè) DOM Document 對(duì)象。

    DOM 語(yǔ)法分析器類(lèi)廠一瞥

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


    清單 3. 使用文檔構(gòu)建器類(lèi)廠
    												
    														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();
    
     //打開(kāi)確認(rèn)選項(xiàng),關(guān)掉名稱(chēng)空間敏感性選項(xiàng)。
     factory.setValidating(true);
     factory.setNamespaceAware(false);
    
     DocumentBuilder builder = factory.newDocumentBuilder();
     Document doc = builder.parse(new File(args[0]));
    
     // 從DOM 數(shù)中打印文檔,并加一初始空格
     printNode(doc, "");
    
     // 在這里也可以對(duì) DOM 文檔進(jìn)行修改
     } 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 樹(shù)
    }
    
    												
    										

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

    使用 DOM 語(yǔ)法分析器

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


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

    是不是感到 DOM 這一節(jié)有些令人厭煩?有這種想法的不止您一個(gè),寫(xiě) DOM 代碼有些令人厭煩是因?yàn)樗侵苯尤〉盟鶎W(xué)的 SAX 知識(shí),然后將其用于 DOM。因此,和朋友、同事打賭吧,說(shuō)使用 JAXP 只是小菜一碟。





    回頁(yè)首


    更改語(yǔ)法分析器

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





    回頁(yè)首


    結(jié)束語(yǔ)

    如您所見(jiàn),沒(méi)多少?gòu)?fù)雜的東西。更改系統(tǒng)屬性,通過(guò)類(lèi)廠、而不是語(yǔ)法分析器或構(gòu)建器來(lái)設(shè)置“確認(rèn)”,以及弄清楚JAXP實(shí)際上不是人們通常所認(rèn)為的那樣,這些是使用 JAXP 的最困難部分。除了沒(méi)有 SAX 2.0 和 DOM第二層規(guī)范支持之外,JAXP 在兩個(gè)流行的 Java 和 XML API 之上提供一個(gè)有幫助的可插入層。它使代碼獨(dú)立于供應(yīng)商,并允許不編譯語(yǔ)法分析代碼而更改語(yǔ)法分析器。那么,從 Sun、Apache XML 或其它方便之處下載 JAXP,并使用它吧!繼續(xù)關(guān)注 JAXP 1.1,并增加對(duì) SAX 2 和 DOM 2、XSLT 及更多內(nèi)容的支持。您將在這里獲得第一手新聞,所以,請(qǐng)關(guān)注 developerWorks

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

    僅列出標(biāo)題  
    主站蜘蛛池模板: 亚洲高清美女一区二区三区| 日本免费电影一区二区| 噜噜嘿在线视频免费观看| 亚洲一级毛片免费看| 91成人免费观看网站| 亚洲美女大bbbbbbbbb| 91福利免费视频| 亚洲视频一区二区在线观看| 最好看的中文字幕2019免费| 久久精品国产亚洲av麻豆小说| 午夜网站在线观看免费完整高清观看| 亚洲av日韩av激情亚洲| 久久久久免费看黄a级试看| 亚洲宅男永久在线| 9420免费高清在线视频| 亚洲熟妇无码久久精品| 在线看片无码永久免费视频| 中文字幕亚洲男人的天堂网络 | 在线亚洲v日韩v| 国产一区视频在线免费观看 | 18以下岁毛片在免费播放| 亚洲精品国产第1页| www.黄色免费网站| 亚洲色大18成人网站WWW在线播放 亚洲色大成WWW亚洲女子 | 成年人免费网站在线观看| 亚洲成熟丰满熟妇高潮XXXXX| 日韩一区二区在线免费观看| 老司机午夜在线视频免费观| 亚洲精品第一国产综合境外资源 | 亚洲国产日韩a在线播放| 国产老女人精品免费视频| 免费人成网站永久| 亚洲精品无码专区在线在线播放| 免费福利电影在线观看| 亚洲国产成AV人天堂无码| 成人免费午夜视频| 日韩毛片在线免费观看| 国产亚洲av片在线观看播放 | 最近中文字幕mv免费高清视频7 | www免费黄色网| 久久亚洲国产精品五月天|