2012年11月13日 星期二

JavaScript - 事件指定function與圓括號(parentheses)的關係

標題其實是想說 elementA.onclick = func; 這樣一回事
之前都已經看慣&習慣這個寫法倒也沒有想太多

直到今天寫了一個要輸入參數的function
function specialSetting(obj){
....   //original code 
}

然後就直覺寫上了 elementA.onclick = specialSetting(elementA);
畢竟一開始學寫JavaScript都是 <div ... onClick="specialSetting(this);" > 這樣開始

然後執行時就發現函式一開始就馬上被呼叫一次...
仔細想想才覺得應該是圓括號的關係
猜測 JavaScript 的執行方式是 elementA.onclick = specialSetting(elementA);
先執行一次呼叫 specialSetting(obj); 之後才綁定事件
function在JavaScript中是物件
elementA.onclick = specialSetting;  表明事件綁了個函式物件,而不是要立刻執行函式

那如果要傳入參數呢? 改用匿名函式就好了
elementA.onclick = functionn(){ specialSetting(elementA); };

回想要傳入事件作參數給函式的作法是
elementA.onclick = functionn(evt){ func(); };
從這裡其實就看得出端倪了

JavaScript 常用事件整理

常見事件整理:

事件                       觸發時機

onabort                 圖片下載被中斷時

onblur                    當游標離開元件時

onchange             當使用者更改欄位內容時

onclick                 滑鼠單擊物件時

ondblclick            滑鼠雙擊物件時

onDragDrop       物件被拉入瀏灠器視窗時

onerror                 載入文件或圖片發生錯誤時

onfocus                當游標定位在元件上時

onkeydown          當鍵盤的按鍵被壓下時

onkeypress         當鍵盤的按鍵被壓下再放開時

onkeyup              當鍵盤的按鍵被放開時

onload                 當網頁或圖片載入完成時

onmousedown     滑鼠按鍵被壓下時

onmousemove     滑鼠指標移動時

onmouseout        滑鼠指標離開物件時

onmouseover      滑鼠指標移到物件上時

onmouseup         滑鼠按鍵被放開時

onreset                當reset按鍵被點擊時

onresize               當視窗或框架被改變大小時

onselect               當文字被選取時

onsubmit              當sumbit按鍵被點擊時

onunload              當使用者離開畫面時

2012年11月12日 星期一

Tomcat,Apache配置GZip壓縮(HTTP壓縮)功能

*修改自網路文章,文章源頭不確定

HTTP 壓縮可以大大提高瀏覽網站的速度
它的原理是,在客戶端請求網頁後,從Server將網頁文件壓縮
再下載到Client,由Client的瀏覽器負責解壓縮並瀏覽
相對於普通的瀏覽過程HTML ,CSS,JavaScript , Text ,它可以節省40%左右的流量
更為重要的是,它可以對動態生成的
包括CGI、PHP、JSP、ASP、Servlet、SHTML等輸出的網頁也能進行壓縮

一,對於Tomcat5.0以後的版本支持對輸出內容進行壓縮,使用的是gzip壓縮格式.
要使用gzip壓縮功能,你可以在Connector實例中加上如下屬性即可

1. compression="on"                                      打開壓縮功能
2. compressionMinSize="2048"                      啟用壓縮的輸出內容大小,這裡預設2KB
3. noCompressionUserAgents="gozilla, traviata"  對於以下的瀏覽器,不啟用壓縮
4. compressableMimeType="text/html,text/xml"   壓縮類型

下面是$tomcat_home$/conf/server.xml的配置內容

<Connector port="8080" protocol="HTTP/1.1"
        compression="on"
        compressionMinSize="2048"
        connectionTimeout="20000"
compressableMimeType="text/html,text/xml,text/plain,
                  text/css,text/json,text/javascript,
                application/x-javascript,application/javascript,
                application/jsp,application/json,image/jpeg,image/png"
         redirectPort="8443" />

對於某些text文件比如:log、txt等,我們也可以讓Server採用gzip壓縮傳輸
修改conf目錄下web.xml,添加

    <mime-mapping>
     <extension>log</extension>
     <mime-type>text/plain</mime-type>
    </mime-mapping>

就可以指定壓縮傳輸了
通常情況下,壓縮傳輸能大幅度提高展示速度

二,對於Apache而言,有兩種情況
1.針對Apache2.0之前的版本,它原本是不支持的
   不過可以通過添加第三方的module_gzip模組來啟用
2.針對Apache2.0及之後的版本,Apache提供支持;不過不叫gzip,而叫mod_deflate

下面就對Apache2.0及之後的版本作一個說明
1. 去掉#LoadModule headers_module modules/mod_headers.so前面的註釋#,
2. 添加LoadModule deflate_module modules/mod_deflate.so
3. 在VirtualHost中添加

    <Location "/">
         SetOutputFilter DEFLATE
         BrowserMatch ^Mozilla/4 gzip-only-text/html
         BrowserMatch ^Mozilla/4\.0[678] no-gzip
         BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
         SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
         Header append Vary User-Agent env=!dont-vary
    </Location>

以下為完整範例

# 加載deflate模組
LoadModule headers_module modules/mod_headers.so
LoadModule deflate_module modules/mod_deflate.so
<VirtualHost *:80>
     DocumentRoot f:/apacheTest
    <Location "/">
         SetOutputFilter DEFLATE
         BrowserMatch ^Mozilla/4 gzip-only-text/html
         BrowserMatch ^Mozilla/4\.0[678] no-gzip
         BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
         SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png)$ no-gzip dont-vary
         Header append Vary User-Agent env=!dont-vary
    </Location>
</VirtualHost>

資料結構容量大小的調整

StringBuilder 和 StringBuffer 底層都使用char[]來做為資料儲存庫
而且常常需要調整容量大小
調整容量大小的方法就是配置一個容量更大的新的char[]
接著將舊資料複製過來後再拋棄舊的char[]
類似的調整過程也可能發生在使用陣列當作底層資料儲存庫的Java SE Collection

依OpenJDK的實作方式
當儲存文字超過底層資料儲存庫的容量時
會配置一個原本容量兩倍大的新char陣列,而底層的char陣列大小預設為16
實務上很少StringBuffer 或StringBuilder的物件最後只使用了16以內的char元素
要避免StringBuffer 或StringBuilder調整大小的動作,我們可以指定大小給其建構子

如ArrayList、Vector、HashMap和ConcurrenthashMap等Collections因使用陣列儲存資料
同StringBuffer 或StringBuilder,很容易在增加元素時引發調整大小的動作
需要額外的CPU週期來配置新陣列、複製舊陣列的資料,將來也需要回收舊陣列
以HashMap為例,預設建構子的容量是16筆資料,也是經常不夠用
其資料擴張時也是使用原本陣列2倍大的新陣列
又因每次調整時都要計算雜湊(hash)值,當資料量大時耗費的成本也更驚人
遇到這種情形時,比較好的處理方式就是直接將計算容量的算式傳給HashMap的建構子

2012年11月10日 星期六

[轉載] require.js的用法

原文


一、為什麼要用require.js?

最早的時候,所有Javascript代碼都寫在一個文件裡面,只要加載這一個文件就夠了
後來,代碼越來越多,一個文件不夠了,必須分成多個文件,依次加載
下面的網頁代碼,相信很多人都見過

      <script src="1.js"></script>
      <script src="2.js"></script>
      <script src="3.js"></script>
      <script src="4.js"></script>
      <script src="5.js"></script>
      <script src="6.js"></script>

這段代碼依次加載多個js文件

這樣的寫法有很大的缺點
首先,加載的時候,瀏覽器會停止網頁渲染
加載文件越多,網頁失去響應的時間就會越長
其次,由於js文件之間存在依賴關係,因此必須嚴格保證加載順序
比如上例的1.js要在2.js的前面
依賴性最大的模塊一定要放到最後加載
當依賴關係很複雜的時候,代碼的編寫和維護都會變得困難

require.js的誕生,就是為瞭解決這兩個問題:
(1)實現js文件的異步加載,避免網頁失去響應;
(2)管理模塊之間的依賴性,便於代碼的編寫和維護。


二、require.js的加載

使用require.js的第一步,是先去官方網站下載最新版本。

下載後,假定把它放在js子目錄下面,就可以加載了。

      <script src="js/require.js"></script>

有人可能會想到,加載這個文件,也可能造成網頁失去響應
解決辦法有兩個,一個是把它放在網頁底部加載,另一個是寫成下面這樣:

      <script src="js/require.js" defer async="true" ></script>

async屬性表明這個文件需要異步加載,避免網頁失去響應
IE不支持這個屬性,只支持defer,所以把defer也寫上

加載require.js以後,下一步就要加載我們自己的代碼了
假定我們自己的代碼文件是main.js,也放在js目錄下面
那麼,只需要寫成下面這樣就行了:

      <script src="js/require.js" data-main="js/main"></script>

data-main屬性的作用是,指定網頁程序的主模塊
在上例中,就是js目錄下面的main.js,這個文件會第一個被require.js加載
由於require.js默認的文件後綴名是js,所以可以把main.js簡寫成main


三、主模塊的寫法

上一節的main.js,我把它稱為"主模塊",意思是整個網頁的入口代碼
它有點像C語言的main()函數,所有代碼都從這兒開始運行

下面就來看,怎麼寫main.js。

如果我們的代碼不依賴任何其他模塊,那麼可以直接寫入javascript代碼。

      // main.js
      alert("加載成功!");

但這樣的話,就沒必要使用require.js了
真正常見的情況是,主模塊依賴於其他模塊
這時就要使用AMD規範定義的的require()函數

      // main.js
      require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
        // some code here
      });

require()函數接受兩個參數
第一個參數是一個數組,表示所依賴的模塊,上例就是['moduleA', 'moduleB', 'moduleC']
即主模塊依賴這三個模塊
第二個參數是一個回調函數,當前面指定的模塊都加載成功後,它將被調用
加載的模塊會以參數形式傳入該函數,從而在回調函數內部就可以使用這些模塊

require()異步加載moduleA,moduleB和moduleC,瀏覽器不會失去響應
它指定的回調函數,只有前面的模塊都加載成功後才會運行,解決了依賴性的問題

下面,我們看一個實際的例子

假定主模塊依賴jquery、underscore和backbone這三個模塊,main.js就可以這樣寫:

      require(['jquery', 'underscore', 'backbone'], function ($, _, Backbone){
        // some code here
      });

require.js會先加載jQuery、underscore和backbone,然後再運行回調函數
主模塊的代碼就寫在回調函數中


四、模塊的加載

上一節最後的示例中,主模塊的依賴模塊是['jquery', 'underscore', 'backbone']
默認情況下,require.js假定這三個模塊與main.js在同一個目錄
文件名分別為jquery.js,underscore.js和backbone.js,然後自動加載

使用require.config()方法,我們可以對模塊的加載行為進行自定義
require.config()就寫在主模塊(main.js)的頭部
參數就是一個對象,這個對象的paths屬性指定各個模塊的加載路徑

      require.config({
        paths: {
          "jquery": "jquery.min.js",
          "underscore": "underscore.min.js",
          "backbone": "backbone.min.js"
        }
      });

上面的代碼給出了三個模塊的文件名,路徑默認與main.js在同一個目錄(js子目錄)
如果這些模塊在其他目錄,比如js/lib目錄,則有兩種寫法。一種是逐一指定路徑

      require.config({
        paths: {
          "jquery": "lib/jquery.min.js",
          "underscore": "lib/underscore.min.js",
          "backbone": "lib/backbone.min.js"
        }
      });

另一種則是直接改變基目錄(baseUrl)。

      require.config({
        baseUrl: "js/lib",
        paths: {
          "jquery": "jquery.min.js",
          "underscore": "underscore.min.js",
          "backbone": "backbone.min.js"
        }
      });

如果某個模塊在另一台主機上,也可以直接指定它的網址,比如:

      require.config({
        paths: {
          "jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
        }
      });

require.js要求,每個模塊是一個單獨的js文件
這樣的話,如果加載多個模塊,就會發出多次HTTP請求,會影響網頁的加載速度
因此require.js提供了一個優化工具
當模塊部署完畢以後,可以用這個工具將多個模塊合併在一個文件中,減少HTTP請求數


五、AMD模塊的寫法

require.js加載的模塊,採用AMD規範
也就是說,模塊必須按照AMD的規定來寫

具體來說,就是模塊必須採用特定的define()函數來定義
如果一個模塊不依賴其他模塊,那麼可以直接定義在define()函數之中

假定現在有一個math.js文件,它定義了一個math模塊。那麼,math.js就要這樣寫:

      // math.js
      define(function (){
        var add = function (x,y){
          return x+y;
        };
        return {
          add: add
        };
      });

加載方法如下:

      // main.js
      require(['math'], function (math){
        alert(math.add(1,1));
      });

如果這個模塊還依賴其他模塊,那麼define()函數的第一個參數,必須是一個數組
指明該模塊的依賴性

      define(['myLib'], function(myLib){
        function foo(){
          myLib.doSomething();
        }
        return {
          foo : foo
        };
      });

當require()函數加載上面這個模塊的時候,就會先加載myLib.js文件


六、加載非規範的模塊

理論上,require.js加載的模塊,必須是按照AMD規範、用define()函數定義的模塊
實際上雖然已經有一部分流行的函數庫(比如jQuery)符合AMD規範,更多的庫並不符合
那麼,require.js是否能夠加載非規範的模塊呢?

回答是可以的

這樣的模塊在用require()加載之前,要先用require.config()方法,定義它們的一些特徵

舉例來說,underscore和backbone這兩個庫,都沒有採用AMD規範編寫
如果要加載它們的話,必須先定義它們的特徵

      require.config({
        shim: {
          'underscore':{
            exports: '_'
          },
          'backbone': {
            deps: ['underscore', 'jquery'],
            exports: 'Backbone'
          }
        }
      });

require.config()接受一個配置對象,這個對象除了有前面說過的paths屬性之外
還有一個shim屬性,專門用來配置不兼容的模塊
每個模塊要定義
(1)exports值(輸出的變量名)表明這個模塊外部調用時的名稱
(2)deps數組,表明該模塊的依賴性

比如,jQuery的插件可以這樣定義:

      shim: {
        'jquery.scroll': {
          deps: ['jquery'],
          exports: 'jQuery.fn.scroll'
        }
      }


七、require.js插件

require.js還提供一系列插件,實現一些特定的功能

domready插件,可以讓回調函數在頁面DOM結構加載完成後再運行

      require(['domready!'], function (doc){
        // called once the DOM is ready
      });

text和image插件,則是允許require.js加載文本和圖片文件

      define([
        'text!review.txt',
        'image!cat.jpg'
        ],
        function(review,cat){
          console.log(review);
          document.body.appendChild(cat);
        }
      );

類似的插件還有json和mdown,用於加載json文件和markdown文件

2012年10月30日 星期二

範例 - JavaScript 等比例縮小圖片

max-width、max-height以及這兩個的 min 版本是偶爾能在某些CSS中看到的屬性設定
不過因為IE不會辨識這些屬性,所以就直接寫JavaScript 調整圖片大小

var img = this.getElementsByTagName("img")[0];

img.onload = function(){
    var maxWidth = "300", maxHeight = "80";
 
    if(img.offsetHeight >= (maxHeight / maxWidth) * img.offsetWidth){
    img.style.width = img.offsetWidth / img.offsetHeight * maxHeight + "px";
    img.style.height = maxHeight + "px";
    }else{
img.style.height = img.offsetHeight / img.offsetWidth * maxHeight + "px";
img.style.width = maxWidth + "px";
    }
}

轉換AJAX取得的XML結構為DOM操作結構

這次的目的是要用AJAX取得的 responseText ,作為節點底下的結構
取得的 responseText 會是 <div>....</div><div>...</div> 這樣的結構
再以DOM方式去操作

通常查詢到的都是用framework的解決方式
例如JQuery - $("...").html( responseText );
Pototype.js也有update() method可以滿足這類需求

不過...沒看到用native JavaScript的過程
想想也是啦...畢竟這樣應該就是要自己去解析字串了
查到較相近的是XML結構的操作
雖然不符合我的需求,但還是值得記錄起來

xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","rss.xml",false);
xmlhttp.send();
xmlText = xmlhttp.responseText;

if (window.DOMParser){
 parser = new DOMParser();
 xmlDoc = parser.parseFromString(xmlText,"text/xml");
} else{
 xmlDoc = new ActiveXObject("Microsoft.XMLDOM");  //for IE
 xmlDoc.loadXML(xmlText);
}

如果是直接取得XML的話用以下形式即可

xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET","rss.xml",false);
xmlhttp.send();
xmlDoc = xmlhttp.responseXML;

2012年10月29日 星期一

Python 基礎-List 介紹 與常見BIF示範

Python保存資料使用的是類似陣列的清單結構
但型別是由Python編譯器處理
Python的List能混合存入各類型的資料,如字串和數字混合放置
另外List中也能包含其他的list,就像多重陣列一樣

Python的多行註解形式是 """註解""" ,單行註解則用#
在編譯器中執行以下程式碼

initList = []         #建立空的List,也可以用BIF方式建立 initList = list()
lists = ["new", "show", "Eclipse"]
demo = ["aa", ["bb", "cc"]]
print(initList)
print(lists[1])    
print(len(lists))     #這裡用上了len() BIF回傳List長度
print(demo[1])      

"""
output:

[]
show
3
['bb', 'cc']
"""

Python稱呼它的內建方法為BIF(built-In Function),常看到的還有

  函式                        說明                                    範例

append()   -   將一筆資料附加到尾端          lists.append("lol")
pop()        -   從最尾端取出一筆資料          lists.pop()           #output:lol
extend()    -   加入一群資料                       lists.extend(["hi", "hello"])
remove()   -   移除特定的資料項                 lists.remove("hi")
insert()      -   插入一筆資料                       lists.insert(0, "head")
list()         -   用於新建空清單的函式
len()         -   取得List長度                         len(List)
range()     -    回傳一個Iterator                    for num in range(4):  #output:0 1 2 3
                   並依照給定的範圍產生數字     print(num)
sort()       -   將List排序,由低到高
reverse()   -  逆轉陣列的順序
count()     -   回傳某個值在List中出現的次數

sum()      -   會將引數中的List數字作加總        sum(list)
randint()   -   前回介於兩引數之間的任意整數      randint(1, 10)
int()        -   將一個字串或數字轉換為整數
float()      -   將一個字串或數字轉換為浮點數
str()        -   將數字轉換成字串
id()         -   傳回一個資料物件在Python中的獨特編號
input()     -   取得並回傳使用者輸入

enumerate() - 補足Iterator的index值,使用方法如下:
           for i, n in enumerate([1, 3, 5]):
       print(i, n)

"""  output
0 1
   1 3
   2 5
"""

zip()     -   整合多個Lists,使用方法如下:
          names = ["John", "Marry", "Tom"]
          sexes = ["Male", "Female", "Male"]
          for name, sex in zip(names, sexes):
              print(name, sex)

"""  output
John Male
   Marry Female
                        Tom Male
"""

Python 安裝配置與 Eclipse PyDev 設定

參考文章:Python: Eclipse+Pydev環境安裝教學
                 在 Windows 7 環境安裝 Python 2.6.6

本篇文章介紹 Python 3 在 Windows 環境的安裝配置過程
另外開發過程會以PyDev作為IDE

首先到Python官網下載需要的版本
安裝後到 [電腦] -> 右鍵[內容] -> [進階系統設定] -> [環境變數]  中添加環境變數
環境變數的設定過程類似 Java


PYTHON_HOME :C:\Python33
PATH: ;%PYTHON_HOME%    (附加在現有內容後)


新建立PYTHON_HOME變數後,在PATH變數加上上述內容就完成了
要確認的話可以到 cmd (命令提示字元) 中輸入 Python 看看

Python雖然可以多個版本並存,但能設定在PATH裡的只能有一個版本
設定主要開發版本即可

Python本身有自帶 IDLE 可以做為簡單版的編譯器使用
它有幾個簡單的快捷鍵:

TAB鍵可以提示尚未輸入完的程式碼
Alt + P 可以召回前一組輸入的程式碼
Alt + N 移往下一個程式碼


不過因為Python是個極端重視縮排的語言
往往縮排就可以讓人在除錯過程中搞得要死不活
建議還是用IDE進行開發
個人因常使用Eclipse而選擇用其平台的PyDev

在Eclipse中安裝 plugin
[Help] -> [Install New software]  加入
name: Pydev
url: http://pydev.org/updates

就個人遇過的情形 Aptana 跟 Pydev 的安裝會有衝突
因為兩者套件都各自有引用對方的部分
最後我是只選擇安裝PyDev

安裝完後到 [Window] –> [PyDev] -> [Interpreter – Python] 設定Python編譯器的路徑
加入 Python 安裝目錄底下的所有東西就好了
之後開發時選擇Python專案就可以開始開發Python了

2012年10月24日 星期三

Java 數值轉換函式區別 - 以Integer.parseInt()、Integer.valueOf()為例

之前對於Java在數值轉換中為什麼會提供這兩種功能幾乎一樣的方法感到疑惑
看了原始碼就可以得到解答
public static Integer valueOf(String s) throws NumberFormatException {
  return new Integer(parseInt(s, 10));
}

public static int parseInt(String s) throws NumberFormatException {
  return parseInt(s,10);
}

答案在於回傳型別的不同
parseInt()回傳 int 基本型別,valueOf()則回傳Integer型別
除了Integer外,其他的數值型別也有同樣的方法(Double、Float...etc)

在Java1.4版本以前這兩種方法有差異,但在後來版本裡因為有auto boxing機制而沒差了

2012年10月22日 星期一

JavaScript 少用全域變數的理由

在 JavaScript: The Good Parts 書中即有討論到這個議題(心得文連結)
容易造成變數名稱紊亂這點,可以盡量用匿名函式的寫法解決
在匿名函式中使用的變數名稱就不會跟全域變數產生衝突
但除了這個問題外,還有效能的考量
JavaScript搜尋變數的方式是 區域變數 -> 更上層的區域變數 -> .... -> 全域變數
若有常用到的全域變數,把它另存成區域變數對效能有很大的幫助

function displayData(){
  //caching variables
  var _ctrl=ctrl;
  var _nui=nui;
  ....
}

Excel 開啟產出的 .CSV檔為亂碼的解決方式

CSV檔為以逗號區隔欄位,用來儲存資料的純文字格式
指定了編碼的純文字格式應該是與亂碼無緣
但有時以微軟的Excel開啟.csv檔卻會發現內容為亂碼
發生的原因是微軟內部系統處理格式時會在開頭暗中寫入一串判別的字碼
而微軟系統就以文字檔案中是否含這串暗碼來判定要以UTF-8或ASCII格式讀取檔案

所以我們若要讓我們產出的CSV檔能正確的被Excel讀取,就要在檔案開頭加入暗碼


public CSVWriter(OutputStream out){
    this.out=out;

    //Corresponding to Microsoft Excel, make file won't open with scrambles in Excel
    byte[] BOM_UTF8 = { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF };
    try {
    out.write(BOM_UTF8);
    } catch (IOException e) {
    System.err.println("BOM寫入CSV失敗");
    }
    ....
}

2012年10月16日 星期二

IE 中checkbox的checked=true不打勾問題解決


這個問題是發現在IE中會沒辦法看到checkbox的選項被打勾
檢查其屬性checked=true無誤

追查下發現原因為在該checkbox尚未被IE初始化(還沒加入DOM樹中)前設定此屬性

解決方法有兩種 -
1.將其加入DOM樹中再設定屬性
2.改為設定其 defaultChecked=true

2012年10月15日 星期一

動態加入CSS 與 IE 的 innerHTML 屬性討論

這幾天有遇到一個情況是動態加入CSS時發現在IE會遇到問題
查過後才發現 IE 會限制部分Element的 innerHTML 屬性值的設定
col, colgroup, frameset, html, style, table, tbody, tfoot, thead, title, tr
上述標籤的innerTML attribute 在 IE 中均為唯讀

以下有兩種處理方式可通用在所有瀏覽器中

1.
....
    var se = document.getElementById("ieStyleApplier");
    se.innerHTML = "";
    se.innerHTML = "_<style>" + o.responseText + "</style>";
...

先設定一個空的div element
再再將CSS內容將入到div的範圍內用(本例中的o.responseText 為Ajax取得CSS內容)
注意stye tag 前加入 '_' 作為前綴字元(prefix character)
這是避免IE解析div element時讀到style tag會停止解析的情形
猜測這是IE的安全性考量
加入前綴字元後就沒問題了,也可以對應到其他的browser

2.

  (function(o){
      var newStyle = document.createElement("link");
      newStyle.rel = "stylesheet";
      newStyle.href = o;
      var head = document.getElementsByTagName('head')[0];
      if(head)
          head.appendChild(newStyle);
      else
          document.body.appendChild(newStyle);
  })(url);

用一次性的函式動態加入link element後引入CSS

2012年10月8日 星期一

跨網域資料取得解決方式 - JSONP

參考文章:【原創】說說JSON和JSONP,也許你會豁然開朗,含jQuery用例
                      使用 JSONP 跨站請求

JSON為JavaScript中常用的資料交換格式
簡單、輕型的特性讓他成為Web 開發的首選,這裡就不多提了

而在Web開發的過程中,我們有時會遇到跨網域存取資料的需求
不管是基於分散流量需求,又或者是使用雲端提供業者的服務
使用第三方提供的服務、資源
甚至在同一個專案目錄底下開發的檔案也可能因啟用HTTPS而導致網域的不同

我們有這麼多會碰上跨網域的情況
但Browser 本身的安全機制會禁止我們直接從其他網域取得資料
瀏覽器唯一能從其他網域取得資源的機會就是JavaScript的匯入
也因此衍伸出一個非官方的解決方案,也就是JSONP
JSONP的全名是JSON with Padding
這項技巧本身與JSON沒有實際關係,僅僅是因為JSON是實作上最使用的格式

JSONP簡單的來說即是利用JavaScript的動態載入
將JSON格式的資料讀取進來後,直接在JavaScript做處理
這與 AJAX是不同的技術方式
AJAX的目的是獲取目標頁面的資料,而JSONP則是獲取一段JavaScript程式碼

欲取得資料的頁面寫下類似下面這段程式碼:

    var head = document.getElementsByTagName('head')[0];
    var storeList = null;

//倒入JSON資料到storeList
var JSONDataGetter = function(data){
storeList = JSON.parse(JSON.stringify(data));
};

var src = "firmList.jsp";      //JSON資料提供頁面
var js = document.createElement('script');
sJs.setAttribute('src', src);
    if(head)
        head.appendChild(js);
    else
        document.body.appendChild(js);

上面這段的 JSONDataGetter() 會將傳入的JSON資料轉成JavaScript物件
接著是資料提供者的頁面的程式碼
<%
response.setContentType("text/javascript");  //將頁面的型別設定為JavaScript

....  //JSP code

int counter = 0;
out.print("JSONDataGetter([");
    for(Map.Entry<String, HashMap<String, String>> m : totalMap.entrySet()){
    HashMap<String, String> store = m.getValue();
   
    if(counter == 0){
    out.print(String.format("{ \"storeName\" : \"%s\", \"storeType\" : \"%s\", \"storeLocation\" : \"%s\"}", m.getKey(), store.get("storeType"), store.get("storeLocation")));
    }else{
    out.print(String.format(", { \"storeName\" : \"%s\", \"storeType\" : \"%s\", \"storeLocation\" : \"%s\"}", m.getKey(), store.get("storeType"), store.get("storeLocation")));
    }
    counter++;
    }
    out.print("])");

%>


大部分都是字串拼貼的處理,顯示出來大概就是下面這樣

JSONDataGetter([ //JSON資料])

引入的JavaScript code會回頭呼叫 JSONDataGetter() 處理
實務上也會常看到在引入JavaScript的路徑上加上callback變數回傳
如:var src = "firmList.jsp?callback=XXX";
提供 JSON 資料的頁面再根據參數而輸出不同的字串,以便呼叫不同的處理函式
如此一來就能做到更多元的處理
這就是JSONP了

2012年10月3日 星期三

[轉載] POI設置EXCEL單元格格式為文本、小數、百分比、貨幣、日期、科學計數法和中文大寫

轉載來源


以下將要介紹的每一種都會用到這三行中的變量

            HSSFWorkbook demoWorkBook = new HSSFWorkbook();  
            HSSFSheet demoSheet = demoWorkBook.createSheet("The World's 500 Enterprises");  
            HSSFCell cell = demoSheet.createRow(0).createCell(0);

第一種:日期格式

            cell.setCellValue(new Date(2008,5,5));
            //set date format
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            HSSFDataFormat format= demoWorkBook.createDataFormat();
            cellStyle.setDataFormat(format.getFormat("yyyy年m月d日"));
            cell.setCellStyle(cellStyle);

第二種:保留兩位小數格式
            cell.setCellValue(1.2);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00"));
            cell.setCellStyle(cellStyle);

這裡與上面有所不同,用的是HSSFDataFormat.getBuiltinFormat()方法,之所以用這個,是因為0.00是Excel內嵌的格式

第三種:貨幣格式

            cell.setCellValue(20000);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            HSSFDataFormat format= demoWorkBook.createDataFormat();
            cellStyle.setDataFormat(format.getFormat("¥#,##0"));
            cell.setCellStyle(cellStyle);

第四種:百分比格式

            cell.setCellValue(20);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            cellStyle.setDataFormat(HSSFDataFormat.getBuiltinFormat("0.00%"));
            cell.setCellStyle(cellStyle);
  此種情況跟第二種一樣

第五種:中文大寫格式

            cell.setCellValue(20000);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            HSSFDataFormat format= demoWorkBook.createDataFormat();
            cellStyle.setDataFormat(format.getFormat("[DbNum2][$-804]0"));
            cell.setCellStyle(cellStyle);

第六種:科學計數法格式

            cell.setCellValue(20000);
            HSSFCellStyle cellStyle = demoWorkBook.createCellStyle();
            cellStyle.setDataFormat( HSSFDataFormat.getBuiltinFormat("0.00E+00"));
            cell.setCellStyle(cellStyle);
此種情況也與第二種情況一樣

2012年10月2日 星期二

[轉載] Java SE 6.0 內建 Javascript 引擎測試

轉載來源

前言 :
Java SE 6.0 採納 JSR 223 建議,使得 Java 能夠與眾多 Script 語言協同運作
透過 Java 定義的標準 API,能夠讓 Script 直接存取和控制 Java 的物件
而Java 應用程式當中也能夠直接內嵌 Script 環境

範例說明 :
底下將會帶幾個範例, 來說明這個 Java 的 JavaScript 引擎能夠達成的工作 :
- 範例01 : 第一個 Hello World 程式, 直接將 JavaScript 代碼丟入 JS Engine 的 eval 函式執行.
package john.js;

import javax.script.*;

public class JSHello {
    public static void main(String args[]) {
        ScriptEngineManager mgr = new ScriptEngineManager(); // 建立一個 Script 管理員
        ScriptEngine jsEngine = mgr.getEngineByName("JavaScript"); // 取得 Javascript 引擎
        try {
          jsEngine.eval("print('Hello, world!')"); // 透過 Javascript 引擎解譯 print('hello, world') 命令
        } catch (ScriptException ex) {
            ex.printStackTrace();
        }    
    }
}

- 範例02:執行外部 javasript 檔案, 這裡是直接把含 JavaScript 代碼(Hello.js) 的內容透過 InputStream 丟給 JS Engine 的 eval API 執行.
package john.js;

import javax.script.*;
import java.io.*;

public class JSRunExtendFile {
    public JSRunExtendFile() {
        ScriptEngineManager engineMgr = new ScriptEngineManager();
        ScriptEngine engine = engineMgr.getEngineByName("ECMAScript");      
        try {
            InputStream is = new FileInputStream(new File("Hello.js"));
            Reader reader = new InputStreamReader(is);
            engine.eval(reader);
        } catch (ScriptException ex) {
            ex.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        JSRunExtendFile ref = new JSRunExtendFile();  
    }
}

- 範例03:透過 Invocable 介面喚起 Javascript 函數.
package john.js;

import javax.script.*;

public class JSInvokeFunction {

    public static void main(String[] args) {
        ScriptEngineManager engineMgr = new ScriptEngineManager();
        ScriptEngine jsEngine = engineMgr.getEngineByName("ECMAScript");
        try {
            jsEngine.eval("function sayHello() {"
                    + "  println('Hello, world!');" + "}");
            Invocable invocableEngine = (Invocable) jsEngine;
            invocableEngine.invokeFunction("sayHello");
        } catch (ScriptException ex) {
            ex.printStackTrace();
        } catch (NoSuchMethodException nsme) {
            nsme.printStackTrace();
        }
    }
}

- 範例04:Script 直接存取 Java 物件
package john.js;

import javax.script.*;
import java.util.*;

public class JSAccessJavaObject {

    public static void main(String[] args) {
        List namesList = new ArrayList();
        namesList.add("Jill");
        namesList.add("Bob");
        namesList.add("Laureen");
        namesList.add("Ed");
        ScriptEngineManager engineMgr = new ScriptEngineManager();
        ScriptEngine jsEngine = engineMgr.getEngineByName("ECMAScript");
        jsEngine.put("namesListKey", namesList); // 將 java 變數指定給 javascript 環境中的
                                                    // nameListKey 變數
        System.out.println("Executing in script environment...");
        try {
            // 列印 Java 的 nameList 變數內容,並新增一個 Dana 字串資料到 List 尾端
            jsEngine.eval("var x;" + "var names = namesListKey.toArray();"
                    + "for(x in names) {" + "  println(names[x]);" + "}"
                    + "namesListKey.add(\"Dana\");");
        } catch (ScriptException ex) {
            ex.printStackTrace();
        }
        System.out.println("Executing in Java environment...");
        for (String name : namesList) {
            System.out.println(name);
        }
    }
}

- 範例05:透過 Invocable 將參數值傳遞給 Javascript 中的函數
package john.js;

import javax.script.*;
import java.util.*;

public class JSPassValueToFunction {

    public static void main(String[] args) {
        List namesList = new ArrayList();
        namesList.add("Jill");
        namesList.add("Bob");
        namesList.add("Laureen");
        namesList.add("Ed");
        ScriptEngineManager engineMgr = new ScriptEngineManager();
        ScriptEngine jsEngine = engineMgr.getEngineByName("ECMAScript");
        Invocable invocableEngine = (Invocable) jsEngine;
        try {
            jsEngine.eval("function printNames1(namesList) {" + "  var x;"
                    + "  var names = namesList.toArray();"
                    + "  for(x in names) {" + "    println(names[x]);" + "  }"
                    + "}" + "function addName(namesList, name) {println('addName: Add '+name);"
                    + "  namesList.add(name);" + "}");
            invocableEngine.invokeFunction("printNames1", namesList);
            invocableEngine.invokeFunction("addName", namesList, "Dana");
        } catch (ScriptException ex) {
            ex.printStackTrace();
        } catch (NoSuchMethodException ex) {
            ex.printStackTrace();
        }
    }
}

補充說明 :
* 純種 Java 與 Javascript 結合
將Script整合進應用程式所能達到的彈性空間的確十分強大
如果能在我們開發的系統也提供Scripting的能力,除了強化擴充性之外,整個系統的質感也是提升不少…爽度up!up!
但以往要達成這目標,有個難處,就是解釋並執行Script用的Interpreting Engine那裡來,自己寫顯然是太累了…。
Java 6這回帶來了好消息:Scripting API,在Java系統中結合Scripting再也不難了,你只需要import javax.script.*; ...

[轉載] javax.script包探秘 javascript與java交互執行

轉載來源

這個包用來和JavaScript進行互操作,比如JAVA類可以調用JavaScript中的方法,而JS也可調用 JAVA中的方法.

1、可用的腳本引擎
Java 6提供對執行腳本語言的支持,這個支持來自於JSR223規範,對應的包是javax.script
預設情況下,Java 6只支持javascript腳本,它底層的實現是Mozilla Rhino,它是個純Java的javascript實現
可以通過下面的代碼列出當前環境中支持的腳本引擎:

// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
// evaluate JavaScript code from String
engine.eval("print('Welocme to java world')");
System.out.println();
ScriptEngineManager manager = new ScriptEngineManager();
List<ScriptEngineFactory> factories = manager.getEngineFactories();
for (ScriptEngineFactory f : factories) {
    System.out.println("egine name:" + f.getEngineName() + "\n"
            + "engine version:" + f.getEngineVersion() + "\n"
            + "language name:" + f.getLanguageName());
}

結果輸出:
Welocme to java world
egine name:Mozilla Rhino
engine version:1.6 release 2
language name:ECMAScript

可以看到,Java內置只支持javascript一種腳本
但是,只要遵循 JSR223,便可以擴展支持多種腳本語言
可以從https://scripting.dev.java.net/上查找當前已被支持的腳本的第三方庫

2、hello script
示例如上面的代碼所示。
使用的API還是很簡單的,ScriptEngineManager是ScriptEngine的工廠,實例化該工廠的時候會加載可用的所有腳本引擎
從工廠中創建ScriptEngine可以使用getEngineByName、getEngineByExtension或 getEngineByMimeType來得到
只要參數名字能對上。執行腳本調用eval方法即可(效果等同於javascript中的eval)

3、傳遞變量
可以向腳本中傳遞變量,使得Java代碼可以和腳本代碼交互,示例如下:

// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
engine.put("a", 4);
engine.put("b", 6);
try {
    Object maxNum = engine
            .eval("function max_num(a,b){return (a>b)?a:b;}max_num(a,b);");
    System.out.println("max_num:" + maxNum);
} catch (Exception e) {
    e.printStackTrace();
}
輸出內容:max_num:6

對於上面put的變量,它作用於自身engine範圍內,也就是ScriptContext.ENGINE_SCOPE
put 的變量放到一個叫Bindings的Map中,可以通過 engine.getBindings(ScriptContext.ENGINE_SCOPE).get(「a」);得到put的內容
和 ENGINE_SCOPE相對,還有個ScriptContext.GLOBAL_SCOPE 作用域
其作用的變量是由同一ScriptEngineFactory創建的所有ScriptEngine共享的全局作用域。

4、動態調用
上面的例子中定義了一個javascript函數max_num,可以通過Invocable接口來多次調用腳本庫中的函數
Invocable接口是 ScriptEngine可選實現的接口。下面是個使用示例:

// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
try {
    engine.eval("function max_num(a,b){return (a>b)?a:b;}");
    Invocable invoke = (Invocable) engine;
    Object maxNum = invoke.invokeFunction("max_num", 4, 6);
    System.out.println(maxNum);
    maxNum = invoke.invokeFunction("max_num", 7, 6);
    System.out.println(maxNum);
} catch (Exception e) {
    // TODO: handle exception
}

上面的invokeFunction,第一個參數調用的腳本函數名,後面跟的可變參數是對應的腳本函數參數
Invocable還有個很酷的功能,就是動態實現接口,它可以從腳本引擎中得到Java Interface 的實例
也就是說,可以定義個一個Java接口,其實現是由腳本完成
以上面的例子為例,定義接口JSLib,該接口中的函數和javascript中的函數簽名保持一致:

public interface JSLib {
    public int max_num(int a, int b);
}
[java] view plaincopyprint?
// create a script engine manager
ScriptEngineManager factory = new ScriptEngineManager();
// create a JavaScript engine
ScriptEngine engine = factory.getEngineByName("JavaScript");
try {
    engine.eval("function max_num(a,b){return (a>b)?a:b;}");
    Invocable invoke = (Invocable) engine;
    JSLib jslib = invoke.getInterface(JSLib.class);
    int maxNum = jslib.max_num(4, 6);
    System.out.println(maxNum);
} catch (Exception e) {
    // TODO: handle exception
}

5、使用Java對象
可以在javascript中使用Java代碼,這確實是很酷的事情
在Rhino中,可以通過importClass導入一個類,也可以通過importPackage導入一個包,也可以直接使用全路經的類
在創建對象時,new也不是必須的。示例代碼如下:

try {
    String script = "var list = java.util.ArrayList();list.add(\"kafka0102\");print(list.get(0));";
    engine.eval(script);
} catch (Exception e) {
    e.printStackTrace();
}

又例如:

try {
    engine.eval("importPackage(javax.swing);" + "var optionPane = "
            + "  JOptionPane.showMessageDialog(null, 'Hello, world!');");
} catch (ScriptException ex) {
    ex.printStackTrace();
}

6、編譯執行
腳本引擎默認是解釋執行的,如果需要反覆執行腳本,可以使用它的可選接口Compilable來編譯執行腳本,以獲得更好的性能,示例代碼如下:

try {
    Compilable compEngine = (Compilable) engine;
    CompiledScript script = compEngine
            .compile("function max_num(a,b){return (a>b)?a:b;}");
    script.eval();
    Invocable invoke = (Invocable) engine;
    Object maxNum = invoke.invokeFunction("max_num", 4, 6);
    System.out.println(maxNum);
} catch (Exception e) {
    e.printStackTrace();
}

7、總結
除了上面提到的特性,腳本引擎還有一些不錯的功能
比如可以執行腳本文件,可以由多線程異步執行腳本等功能
引入腳本引擎,可以對一些配置擴展和業務規則做更強大而靈活的支持,也方便使用者選擇自己熟悉的腳本語言來編寫業務規則等

public class JSTest {
    public static void main(String[] args) throws Exception {
        // 根據js的後綴名生成一個解析JS的腳本解析引擎

        ScriptEngine engin = new ScriptEngineManager()
                .getEngineByExtension("js");
        // 查詢一下這個引擎是否實現了下面很實用的接口

        System.out.println(engin instanceof Invocable);
        // 聲明兩個對象,傳入到JS裡面去

        JFrame jf = new JFrame("test");
        List list = new ArrayList();
        // 得到綁定的鍵值對象,把當前的兩個JAVA對象放進去

        Bindings bind = engin.createBindings();
        bind.put("jf", jf);
        bind.put("list", list);
        // 把綁定下的鍵值對象放進去,作用域是當前引擎的範圍

        engin.setBindings(bind, ScriptContext.ENGINE_SCOPE);
        // 用引擎執行一段寫在JS文件裡面的代碼

        Object obj = engin.eval(new FileReader("test.js"));
        // 這個時候返回值當然 是null了

        System.out.println(obj);
        // 把當前的引擎強制轉為Invocable,這樣就可以調用定義在JS文件裡面的一個一個函數了

        Invocable in = (Invocable) engin;
        // 得到了從JS裡面返回來的對象

        List l = (List) in.invokeFunction("getNames");
        System.out.println(l);
        // 調用一下定義在JS裡面的另一個函數

        in.invokeFunction("testJS");
        // 最後調用一個函數,該函數可以使我們前面定義的窗體顯示出來

        in.invokeFunction("doSth");
    }
}
下面是定義在test.js裡面的內容
[javascript] view plaincopyprint?
function doSth()
{
    jf.setSize(500,300);
    jf.setVisible(true);
    jf.setDefaultCloseOperation(jf.EXIT_ON_CLOSE);  
}  
function getNames()
{   
    list.add("doSth");
    list.add("getNames");
    return list;
}  
function testJS()
{
    print('Hello world!');
}
我們可以看到,在JAVA運行了以後,窗體會顯示出來,並且我們可以接收到從JS解析引擎裡面傳回的數據
當然我們也可以調用一個很普通的JS函數
想像一下,如果我們把我們程序運行時的一些對象都設到Bindings裡面去,那麼我們JS豈不是有很大的自由度了嗎?
因為JS裡面也可以操作我們的Java對象了,並且我們可以像ava編程一樣的對JS編程了,還不用再編譯,馬上就可以運行.靈活性豈不是變得更高了嗎?

參考自:
【1】javax.script包探秘 javascript與java交互執行
http://blog.csdn.net/lvshow/article/details/7252236
【2】Scripting for the Java Platform
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/

2012年9月20日 星期四

POI 自訂方法

copyRow() - 複製Row的method
updateFormula() - 計算Excel公式結果的方法

複製Row的method

POI有cloneSheet()這個複製sheet的方便方法
但是卻缺少複製sheet裡的row的方法
當然考量到公式的計算與合併的儲存格的位置
這個方法須自訂是不太令人意外

private void copyRow(HSSFSheet worksheet, int sourceRowNum, int destinationRowNum) {
    // Get the source & new row
    HSSFRow newRow = worksheet.getRow(destinationRowNum);
    HSSFRow sourceRow = worksheet.getRow(sourceRowNum);

    // If the row exist in destination, push down all rows by 1 else create a new row
    if (newRow != null) {
        worksheet.shiftRows(destinationRowNum, worksheet.getLastRowNum(), 1);
    } else {
        newRow = worksheet.createRow(destinationRowNum);
    }

    // Loop through source columns to add to new row
    for (int i = 0; i < sourceRow.getLastCellNum(); i++) {
        // Grab a copy of the old/new cell
        HSSFCell oldCell = sourceRow.getCell(i);
        HSSFCell newCell = newRow.createCell(i);

        // If the old cell is null jump to next cell
        if (oldCell == null) {
            newCell = null;
            continue;
        }

        // Use old cell style
        newCell.setCellStyle(oldCell.getCellStyle());

        // If there is a cell comment, copy
        if (newCell.getCellComment() != null) {
            newCell.setCellComment(oldCell.getCellComment());
        }

        // If there is a cell hyperlink, copy
        if (oldCell.getHyperlink() != null) {
            newCell.setHyperlink(oldCell.getHyperlink());
        }

        // Set the cell data type
        newCell.setCellType(oldCell.getCellType());

        // Set the cell data value
        switch (oldCell.getCellType()) {
            case Cell.CELL_TYPE_BLANK:
                newCell.setCellValue(oldCell.getStringCellValue());
                break;
            case Cell.CELL_TYPE_BOOLEAN:
                newCell.setCellValue(oldCell.getBooleanCellValue());
                break;
            case Cell.CELL_TYPE_ERROR:
                newCell.setCellErrorValue(oldCell.getErrorCellValue());
                break;
            case Cell.CELL_TYPE_FORMULA:
                newCell.setCellFormula(oldCell.getCellFormula());
                break;
            case Cell.CELL_TYPE_NUMERIC:
                newCell.setCellValue(oldCell.getNumericCellValue());
                break;
            case Cell.CELL_TYPE_STRING:
                newCell.setCellValue(oldCell.getRichStringCellValue());
                break;
        }
    }

    // If there are are any merged regions in the source row, copy to new row
    for (int i = 0; i < worksheet.getNumMergedRegions(); i++) {
        CellRangeAddress cellRangeAddress = worksheet.getMergedRegion(i);
        if (cellRangeAddress.getFirstRow() == sourceRow.getRowNum()) {
            CellRangeAddress newCellRangeAddress =
                  new CellRangeAddress(newRow.getRowNum(),
                    (newRow.getRowNum() +
                    (cellRangeAddress.getLastRow() - cellRangeAddress.getFirstRow())
                     ),
                    cellRangeAddress.getFirstColumn(),
                    cellRangeAddress.getLastColumn());
            worksheet.addMergedRegion(newCellRangeAddress);
        }
    }
}


計算Excel公式結果的方法

使用POI填入公式時,Excel表並不會將計算後的結果立刻展示
它的作法是顯示出公式(舊版office)或待開啟編輯權限後才計算(office 2010)
以下方法能計算特定行數的Excel公式


private void updateFormula(HSSFWorkbook wb, HSSFSheet sheet, int row){    
    Row r = sheet.getRow(row);    
    Cell cell = null;    
    HSSFFormulaEvaluator eval = new HSSFFormulaEvaluator(wb);    
    for(int i = r.getFirstCellNum(); i < r.getLastCellNum(); i++){    
        cell = r.getCell(i);    
        if(cell.getCellType()== Cell.CELL_TYPE_FORMULA)    
            eval.evaluateFormulaCell(cell);    
    }    
}


HSSFFormulaEvaluator提供了evaluateFormulaCell(Cell cell)方法
計算公式保存結果,但不改變公式
而evaluateInCell(Cell cell)方法是計算公式,並將原公式替換為計算結果,
也就是說該單元格的類型從Cell.CELL_TYPE_FORMULA改為Cell.CELL_TYPE_NUMBERIC

HSSFFormulaEvaluator提供了靜態方法evaluateAllFormulaCells(HSSFWorkbook wb)
計算一個Excel文件的所有公式,用起來很方便

2012年9月19日 星期三

除錯 - java.lang.IllegalStateException: getOutputStream() called

今天碰上的這個錯誤訊息是在JSP頁面發生

ServletResponse.getOutputStream()
ServetResponse.getWriter()

這兩個方法都會有可能在JSP輸出頁面內容的時候被調用
當其中一個方法被調用過後,再呼叫另一個方法就會報錯誤
解決的方法不外如下:

out.clearBuffer();                           //清除buff
out = pageContext.pushBody();    //更新out的內容