2008年12月18日 星期四

HTC G1 DEVELOPER PHONE (圖)

1. 這就是日前收到的HTC G1 DEVELOPER PHONE, 由於沒有鎖機所以任何電信公司都可以使用。
2. 我覺得比較新奇的是解碼系統像是在玩連連看遊戲, 連成當初設置的路徑才會解開!!
3. 推拉式螢幕與鍵盤的質感都很不賴, 感覺不容易壞掉, 當然show出鍵盤時螢幕會變成水平顯示
4. 收發Gmail非常方便, 如果你有設同步化的話新郵件會立即通知。

其他還有很多功能請參考: http://big5.pconline.com.cn/b5/mobile.pconline.com.cn/review/0810/1463217_4.html

總而言之, 這個版本和市售的不一樣, 以顯現Android為主, 沒有logo, 只有背面有Android機器人圖樣, 非常好用!! 現在我已經準備開始寫程式放到Android Market上去賣啦哈哈!!

       

2008年12月15日 星期一

科技始終來自於人性

還記得Nokia的這句廣告詞嗎? 大概80%的人都知道, 卻不了解他真正的涵義...。

最近家裡準備整修一番, 所以請了些設計師來估價:

第一次: 經驗老到的設計師一進門就開始指東指西"這裡打掉, 陽台推出去, 電視櫃不要了...", 後來我問他"那東西應該收納在哪裡?", 卻得到一句這樣的回應"你不需要太多東西!"。

第二次: 一對夫妻設計師, 從頭到尾一直在說他多厲害, 多會設計, 但他的作品樣本我很確定是另一家公司的設計圖。

第三次: 同樣也是一對夫妻, 看起來經驗沒有很老到, 坐下來談的時候一直詢問"覺得這樣改可以接受嗎?", "收納會不會不方便?", "重要的是你要住的舒服才對!" 等...。

大家覺得我會選擇哪一個呢?

自己也是做程式設計, 一直面對不同的客戶自然而然就會產生"應該照我這樣做"的想法, 覺得這樣最省事也最保險, 但是客戶真的喜歡嗎? 功能再棒的產品不實用或不好用就是失敗...設計了一間非常摩登的房子, 裡頭的主人卻每天住的很憋扭...這樣應該不算成功巴。但我們總是會乎略, 設計一個自己覺得很滿意的作品下次當成展示用, 收了錢好像客戶喜不喜歡又是另一回事...。

真是要好好檢討一下。

2008年12月9日 星期二

Rich Internet Application v.s. Window Application

之前一直都在開發RIA網站, 最近因為公司內部需要轉向使用Java SWT(Standard Widget Toolkit)開發視窗環境的軟體。
其實用SWT開發真的很方便, 組件完整, 事件流好控管, 版面縮放不易出錯等...真的是事倍功半(當然你要有可視化開發介面拉), 比起用ActionScript開發, 如果沒有自己先寫一套框架還真是要從頭開始作苦工。

不過我不是想討論哪個語言好壞, 而是近幾年來軟體越來越難賣, 加上RIA與SaaS的風行, 到底孰重孰輕?

基本上RIA目前已經轉向免費服務居多的方式, 除非一開始就去招募資金或是背後有金主, 否則真的相當難生存, 只是一直在燒錢而已。但是最近有些例子指出, 某方面免費資源分享(音樂, 影像等)的RIA網站開始和唱片公司電影公司合作或直接讓它收購, 這也相當聰明, 與其讓大公司摧殘你, 不如先投誠, 假他人之手免費幫網站宣傳又可以獲得資金援助, 一舉兩得。

或是可以像當前線上遊戲主服務免費收取附加價值的產品, 一人一塊20億就...。

單機應用軟體前景就比較看淡了, 以前沒得選一定要用單機軟體, 現在Web Browser打開就能享受一樣的服務, 雖然還是有一定專屬領域不至於消失, 不過需要安裝又常出問題實為不懂電腦的老百姓一大苦處, 偏偏50億人裡有49.6億大概都是這種人。

可能以後單機軟體走向會改變成彙整網路資源, 畢竟單機的優勢在於能夠享受多一點cpu與memory, 當然要做更偉大的事才能把RIA比下去, 像是可以彙整所有拍賣網站的該產品價格作列表比較等...。

我心中倒是有一張理想圖, 在網路資訊爆炸的年代, RIA負責社群交流與資源分享, 單機軟體負責大量彙整與過濾整理, 各司其職, 天下太平。

以上皆為下班前的天馬行空, 個人愚見^^

Google宣佈推出一款沒有鎖Sim卡與硬體的Android智慧手機

剛又敗了25塊美金到Google去, 為什麼呢? 因為Google宣佈推出一款沒有鎖Sim卡與硬體的Android智慧手機給開發者用...我想都沒想就去註冊拉!!對我們而言還有什麼比能自己寫手機應用軟體兼放到Google上賣錢的好處好呢?

基本上正常銷售時它也是像iPhone那樣綁定服務商, 但是這款開發者版本卻是不限服務商與硬體。

要取得這個開發人員專用的Android Dev Phone 1,要先去Android Market網站註冊成為Android開發者,註冊費為25美元。手機則會收取399美元(美國本土免運費)。為了避免供不應求,Google表示,每個註冊帳戶只能申請一支。

目前該手機提供給18個國際市場申請,包括台灣、美國、英國、德國、日本、印度、加拿大、法國、西班牙、澳洲、新加坡、瑞典、瑞士、荷蘭、奧地利、芬蘭、波蘭、與匈牙利。Google表示未來還會開放更多地方。

想要的人請去註冊http://market.android.com/publish/signup
T-mobile G1 官網: http://www.t-mobileg1.com/

2008年12月8日 星期一

轉: 無所不在的Context Switch

蔡學鏞大哥寫的這篇文章真的是太好了!!因此和各位分享一下...
如果有看過peopleware這本管理經典著作的話, 裡面有提到很多讓工程師分心所帶來的後果之實例。

作者:蔡學鏞

有時候電腦忽然慢下來,這時候打開「工作管理員」(Task Manager),你會驚訝地發現,怎麼有這麼多莫名其妙的「常駐程式」正同時在執行?其中一個罪魁禍首程式佔用了CPU將近百分之百的效能,難怪你的Visual Studio忽然變得這麼慢。

不僅電腦中有許多常駐程式,會來中斷我們的重要程式,我們現實的生活中,也有許多例行與非例行的事項,佔據我們一天中大多數的時間,讓我們做事的效率不彰。可能是沒有結論的無聊會議、可能是一通女友撒嬌的電話、可能是MSN上沒有意義的寒暄、合作廠商禮貌性的拜會、或者行政人員對於你請款單據的刁難。這些都會讓你的工作產能大減。

你知道,將這些亂七八糟的事情摒除於外,絕對可以省下許多時間寫程式,也就不需要天天加班了!但是你可能不知道,你因此省下的時間,不只是這些雜事的時間,還包括了「進入流」的時間。

心理學家發現一種「高度專心與高生產力」的心智狀態,稱之為「流」(flow)。進入流的狀態,需要約15分鐘(0.25小時)的時間。你可以把「進入流」的時間,比喻成CPU進行Context-Switch時的Overhead。

二十多年前,《Peopleware: Productive Projects and Teams》一書,就已經開始提醒我們,程式員寫程式時遇到外來干擾,對生產力的傷害有多大。總而言之,如果你想提升你的程式生產力,你可以全面檢視一天中有哪些Context-Switch,然後盡量避免。

假設有一個程式員,中午休息一個小時,上下午的上班時間各3.5小時。那麼他處於高生產力的時間,大約是3.5 – 0.25 + 3.5 – 0.25 = 6.5 小時。假設一天中被電話等雜事中斷10次,每次都花費6分鐘(0.1小時),則高生產力的時間變成 6.5 – (0.1+0.25) * 10 = 3小時。降低的幅度相當驚人。每次6分鐘的打擾,都會造成 6+15 = 21分鐘的損失。

每一次的注意力轉移,都需要耗費15分鐘才能回到之前的流狀態。所以大多數的程式員,很難在白天上班時有很高的生產力。有許多程式員喜歡在晚上(或下班後)寫程式,正是因為半夜的干擾比白天少很多,所以產能比較高。

程式員要如何避免來自辦公室的干擾?大部分的程式員都沒有獨立的辦公室,只有小方塊,所以很難避免干擾。以前在上班時,我就常常幻想要把辦公室小方塊隔板加高,蓋上屋頂,加個滑動門,成為一個1.5 x 1.5 x 2 立方公尺的「辦公包廂」。我還想加個造型煙囪,兼具透氣的目的。如果真有辦公包廂,應該就可以阻隔相當多來自辦公室的干擾。

許多程式員習慣上班時戴著耳機,當進入耳朵的聲音變得可以預期(例如音樂),大腦可以將它變成白色噪音(white noise),而主動過濾掉。但是我覺得一整天都戴著耳機,耳朵其實會不舒服。加上長期使用耳機會造成聽力衰退,而現代醫學認為,聽力衰退是無法復原的,所以戴著耳機似乎不是好作法。我後來嘗試著改用耳塞,覺得效果還不錯。

Instant Messenger(IM)軟體也常常會造成工作的干擾。我工作時,盡量不開IM,或者設定成忙碌。有些人很白目,對我的「忙碌」狀態置之不理,找我進行不重要的對話,這種人就會被我封鎖。除了IM,電話也常常是干擾的來源。

上述的這些干擾,是我們可以輕易地察覺的,但是有一類干擾,是編程語言與開發工具造成的,也會造成Context-Switch,使得程式員的生產力降低,但是卻少有人注意到。

有些編程語言和開發工具,需要你進行自我測試、除錯、編譯、連結的冗長動作,讓你離開真正具有高生產力的流。大部分這類的語言,都是編譯式、低階、傳統的語言。

Script語言顯然就比較沒這個問題。許多使用Script語言的人都會覺得生產力很高,開發的時間可以比傳統的語言快上許多倍。一方面是因為Script語言很高階,可以用比較簡短的程式碼,做出很多事;另一方面,有問題可以立刻修改,不需要形成中斷而離開「流」。

即使是傳統的語言,也可以藉由開發工具的幫忙,減少離開流,而改進開發效率。例如六七年前的IntelliJ就顯然比當時其他Java IDE更能夠減少Context-Switch,生產力更高,所以馬上成為最受歡迎的Java IDE之一。

請注意:有些行為很容易被誤認為是干擾,但其實是有助於生產力的。例如,很累時,可以起身走動,到辦公室外面的Starbucks買咖啡,但是腦筋依然保持原來的Context,繼續思考,不要進行Context-Switch。當身體在動作時,血液循環加快,腦筋似乎會更清晰。

在走路的時候,盡量不要被沿路的事情所分心,也不要和他人交談,因為這些都會造成Context-Switch。不過你可得小心,因為我有好幾次在去買咖啡的路上,差一點因此被車撞到呢!為了避免Context Switch,而造成主機損毀是不值得的。

2008年12月5日 星期五

JavaFX 1.0 釋出

昨天JavaFX釋出了1.0版本, 在RIA界中JavaFX只是個剛起步的小孩, 無論是比Flash, Flex, SilverLight都略遜一籌, 但我個人認為這兩邊的發展模式不盡相同。

公司派:

1. Adobe 部分目前佔據了大部分市場沒錯, 但對手微軟的急起直追讓Adobe不得不加緊腳步推出新產品, 品質的好壞與進步的方向有待商議, 可能競爭下的結果是兩敗俱傷。
2. Flash ,Flex 等還是有相當大的改進空間, 執行時期效能, 檔案大小等..., 至於SilverLight沒用過不便說明。
3. 例如之前macromedia公司被併購, 走向調整為AS3, 主要發展Flex等, 微軟就不用說了, 每次新產品一出來就產品就被晾一邊不再維護。
4. 足夠經費發展, 進度快很多, 開發出的產品也較完整。
5. 週邊配套措施完善, like FMS etc...。

開源派:

1. 開發進度緩慢, 經費較少。
2. 人才容易被挖角, JavaFX之前develop leader就被挖角到Flex開發團隊。
3. 好處是穩定, 大方向不易改變。
4. 可能OpenSource派工程師比較容易青睞。

再補充一篇蔡學鏞大哥的見解:

由於JavaFX是一個編程語言,我們也可以把JavaFX拿來和Java做比較,JavaFX比Java更簡單、好學、寫程式更快。

DSL有一個好處,就是目標明確,語法簡單。JavaFX讓Java走上一條簡化的道路,或許JavaFX的FX,是Fixed的意思吧!但是C# 3.0 與 LINQ卻帶領C#走向一個越來越複雜的道路。我最近一直在懷疑,C#是不是打算向C++或Ada看齊,把自己變成一個超級複雜的語言。在程式語言的理念上,我比較認同Eiffel、Python、REBOL,維持語言的簡單性。因此,我相當喜歡JavaFX。...

全文在此

Anyway, 各有各的好壞處, 大家選擇自己當下最適用的就好!

2008年12月4日 星期四

Java FunP 自動推文程式

測試了一個禮拜應該是沒有問題了, 發佈出來讓大家參考, 使用此程式先要有apache HttpClient相關套件(請參閱之前發文運用Apache HttpClient實作Get與Post動作)

流程為: 登入FunP之後記住Cookie之外, 進入其他頁面必須還要傳送not_rem_login與expires等驗證資訊, 成功取得頁面內容後使用Regular Expression取得文章代號, 就可以進行推文與收藏的動作, 如果想自己執行網頁流程看看傳輸內容的話建議使用SmartSniff, 可以看見POST或GET傳輸內容與表頭。

FunpAutoLogin:

package org.sam.funp;

import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.PostMethod;

public class FunpAutoLogin
{
    private HttpClient client;
    private FunpPusher pusher;
    private FunpAddFavo addfavo;

    public FunpAutoLogin()
    {
        pusher = new FunpPusher();
        addfavo = new FunpAddFavo();

        // set up the HttpClient object.
        client = new HttpClient();
        // host configuration.
        client.getHostConfiguration().setHost("funp.com", 80, "http");
    }

    public void login()
    {
        PostMethod method = new PostMethod("/account/login.ajax.php");

        // Cookie設置.
        Header newCookieHeader = method.getResponseHeader("Set-Cookie");
        Header currentCookieHeader = method.getRequestHeader("Cookie");
        if (newCookieHeader != null) {
            if (currentCookieHeader == null) {
                method.setRequestHeader("Cookie", newCookieHeader.getValue());
            } else {
                method.setRequestHeader("Cookie", currentCookieHeader.getValue() + "; " + newCookieHeader.getValue());
            }
        }
        // 表頭設置.
        method.setRequestHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.4.154.25 Safari/525.19");  
        method.setRequestHeader("Referer", "http://funp.com/push/");  
        method.setRequestHeader("Cache-Control", "max-age=0");  
        method.setRequestHeader("X-Prototype-Version", "1.5.1.1");  
        method.setRequestHeader("X-Requested-With", "XMLHttpRequest");  
        method.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");  
        method.setRequestHeader("Accept", "text/javascript, text/html, application/xml, text/xml, */*");  
        method.setRequestHeader("Accept-Encoding", "gzip,deflate,bzip2,sdch");  
        method.setRequestHeader("X-SDCH", "Chrome 0.4.154.25");  
        method.setRequestHeader("Accept-Language", "zh-TW,zh,en-US,en");  
        method.setRequestHeader("Accept-Charset", "Big5,*,utf-8");  
        method.setRequestHeader("Host", "funp.com");  
        method.setRequestHeader("Content-Length", "69");  
        method.setRequestHeader("Connection", "Keep-Alive");  
    
        // POST傳送資訊.
        NameValuePair login = new NameValuePair("titlebar_login", "");  
        NameValuePair email = new NameValuePair("titlebar_id", your account);  
        NameValuePair passwd = new NameValuePair("titlebar_password", your passwd);  
        NameValuePair no = new NameValuePair("_", "");  
        method.setRequestBody(new NameValuePair[] { login, email, passwd, no });  
  
        try {  
            client.executeMethod(method);  
     
            // get response status.
            int state = method.getStatusCode();  
            if (state == HttpStatus.SC_OK) {  
                System.err.println("> login success.");  
                method.releaseConnection();  
      
                // 每頁推完休息120秒.
                for(int i = 1; i <= 4; i++){  
                    System.err.println("> browsing page " + i + ".");  
                    getPushDetail(1, 1, i);  
                    try{  
                        Thread.sleep(120000);  
                    }catch(Exception e){  
                        e.printStackTrace();  
                    }  
                }  
            } else {  
                System.err.println("> login failed, funP system might be crashed.");  
            }  
        } catch (HttpException httpexc) {  
            System.err.println(">> fatal protocol violation: " + httpexc.getMessage());  
            httpexc.printStackTrace();  
        } catch (IOException ioexc) {  
            System.err.println(">> fatal transport error: " + ioexc.getMessage());  
            ioexc.printStackTrace();  
        }  
    }

    private void getPushDetail(int hot, int stars, int page)
    {
        // 推文首頁.
        // GetMethod get = new GetMethod("/push/index.php?hot=" + hot + "&stars=" + stars + "&page=" + page);

        // 好友推文.
        GetMethod get = new GetMethod("/push/index.php?friend=post&star=&page=" + page);

        // 傳送已登入驗證
        get.setRequestHeader("not_rem_login", "1");
        get.setRequestHeader("expires", "Sun, 28-Dec-2012 13:21:53 GMT");
        get.setRequestHeader("path", "/");
        get.setRequestHeader("domain", "funp.com");
        try {
            client.executeMethod(get);
            String response = readSource(get).toLowerCase().replaceAll("\r", "").replaceAll("\n", "").replaceAll("\t", "");
            String searchDescriptionReg = "<div.class=['\"]description['\"].id=['\"]description_([0-9]+)['\"]>";
            Pattern pattern = Pattern.compile(searchDescriptionReg); // use regular expression to parse image src.
            Matcher matcher = pattern.matcher(response);
            while (matcher.find()) {
                try {
                    String id = matcher.group(1);
                    String description = matcher.group(2);

                    // 推文.
                    pusher.push(client, id, hot, stars, page);
                    // 加入最愛.
                    addfavo.add(client, id, hot, stars, page);

                    // 顯示進度
                    System.out.println("> id: " + id + ", description: " + description);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        } catch (HttpException httpexc) {
            System.err.println(">> Fatal protocol violation: " + httpexc.getMessage());
            httpexc.printStackTrace();
        } catch (IOException ioexc) {
            System.err.println(">> Fatal transport error: " + ioexc.getMessage());
            ioexc.printStackTrace();
        } finally {
            get.releaseConnection();
        }
    }

    // read response html source.
    private String readSource(HttpMethodBase method) throws IOException
    {
        BufferedInputStream inputStream = new BufferedInputStream(method.getResponseBodyAsStream());
        BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
        char[] buff = new char[1024];
        StringBuilder htmlSource = new StringBuilder();
        int c = 0;
        while ((c = bufferedReader.read(buff, 0, 1024)) != -1) {
            htmlSource.append(buff, 0, c);
        }
        // deal with the response.
        String response = htmlSource.toString();

        return response;
    }

    public static void main(String[] args) throws Exception
    {
        new FunpAutoLogin().login();
    }
}

FunpPusher:

package org.sam.funp;

import java.io.IOException;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.NameValuePair;
import org.apache.commons.httpclient.methods.PostMethod;

public class FunpPusher
{
    public FunpPusher()
    {
        System.out.println("> pusher created.");
    }

    public void push(HttpClient client, String no, int hot, int star, int page)
    {
        PostMethod method = new PostMethod("/push/push.ajax.php");

        method.setRequestHeader("not_rem_login", "1");
        method.setRequestHeader("expires", "Sun, 28-Dec-2012 13:21:53 GMT");
        method.setRequestHeader("path", "/");
        method.setRequestHeader("domain", "funp.com");

        method.setRequestHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.4.154.25 Safari/525.19");
        method.setRequestHeader("Referer", "http://funp.com/push/index.php?hot=" + hot + "&stars=" + star + "&page=" + page);
        method.setRequestHeader("X-Prototype-Version", "1.5.1.1");
        method.setRequestHeader("X-Requested-With", "XMLHttpRequest");
        method.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        method.setRequestHeader("Accept", "text/javascript, text/html, application/xml, text/xml, */*");
        method.setRequestHeader("Accept-Encoding", "gzip,deflate,bzip2,sdch");
        method.setRequestHeader("X-SDCH", "Chrome 0.4.154.25");
        method.setRequestHeader("Accept-Language", "zh-TW,zh,en-US,en");
        method.setRequestHeader("Accept-Charset", "Big5,*,utf-8");
        method.setRequestHeader("Host", "funp.com");
        method.setRequestHeader("Content-Length", String.valueOf(7 + no.length()));
        method.setRequestHeader("Connection", "keep-alive");
        method.setRequestHeader("Cache-Control", "max-age=0");

        NameValuePair pid = new NameValuePair("pid", no);
        NameValuePair blank = new NameValuePair("_", "");
        method.setRequestBody(new NameValuePair[] { pid, blank });

        try {
            client.executeMethod(method);

        } catch (HttpException httpexc) {
            System.err.println(">> Fatal protocol violation: " + httpexc.getMessage());
            httpexc.printStackTrace();
        } catch (IOException ioexc) {
            System.err.println(">> Fatal transport error: " + ioexc.getMessage());
            ioexc.printStackTrace();
        } finally {
            method.releaseConnection();
        }
    }
}

FunpAddFavo:

package org.sam.funp;

import java.io.IOException;

import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.GetMethod;

public class FunpAddFavo
{
    public FunpAddFavo()
    {
        System.out.println("> addfavo created.");
    }

    public void add(HttpClient client, String no, int hot, int star, int page)
    {
        GetMethod method = new GetMethod("/push/doFavoPost.ajax.php?type=add&pid=" + no);

        method.setRequestHeader("not_rem_login", "1");
        method.setRequestHeader("expires", "Sun, 28-Dec-2012 13:21:53 GMT");
        method.setRequestHeader("path", "/");
        method.setRequestHeader("domain", "funp.com");

        method.setRequestHeader("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/525.19 (KHTML, like Gecko) Chrome/0.4.154.25 Safari/525.19");
        method.setRequestHeader("Referer", "http://funp.com/push/index.php?hot=" + hot + "&stars=" + star + "&page=" + page);
        method.setRequestHeader("X-Prototype-Version", "1.5.1.1");
        method.setRequestHeader("X-Requested-With", "XMLHttpRequest");
        method.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
        method.setRequestHeader("Accept", "text/javascript, text/html, application/xml, text/xml, */*");
        method.setRequestHeader("Accept-Encoding", "gzip,deflate,bzip2,sdch");
        method.setRequestHeader("X-SDCH", "Chrome 0.4.154.25");
        method.setRequestHeader("Accept-Language", "zh-TW,zh,en-US,en");
        method.setRequestHeader("Accept-Charset", "Big5,*,utf-8");
        method.setRequestHeader("Host", "funp.com");
        method.setRequestHeader("Connection", "keep-alive");

        try {
            client.executeMethod(method);
        } catch (HttpException httpexc) {
            System.err.println(">> Fatal protocol violation: " + httpexc.getMessage());
            httpexc.printStackTrace();
        } catch (IOException ioexc) {
            System.err.println(">> Fatal transport error: " + ioexc.getMessage());
            ioexc.printStackTrace();
        } finally {
            method.releaseConnection();
        }
    }
}

Java SQLParser SQL語法解析器

最近案子因為寫了一個輕便型的小型資料庫, 所以要用的SQL指令解析來動作, 基本上這支程式解析簡單的一層SQL指令沒有問題, 多層則是不行使用的。

剛好順便複習一下Regular Expression, 都快不認識它了哈哈!!

Sample Code:

import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class SqlParser
{
    public static String type;
    public static String conditions;
    public static String tables;
    public static String order;
    public static String where;

    public static void parse(String sql)
    {
        String regex = "";

        type = "";
        conditions = "";
        tables = "";
        order = "";
        where = "";

        if (sql.indexOf("select") == 0) {

            type = "select";
            if (isContains(sql, "\\s+where\\s+")) {
                regex = "(select)(.+)(from)(.+)(where*)";
            } else if (isContains(sql, "\\s+order\\s+")) {
                regex = "(select)(.+)(from)(.+)(order*)";
            } else {
                regex = "(select)(.+)(from)(.+)($)";
            }
            tables = getMatchedPosition(regex, sql, 4);
            conditions = getMatchedPosition(regex, sql, 2);
            order = getOrderIndex(sql);
            where = getWhere(sql);

        } else if (sql.indexOf("insert") == 0) {

            type = "insert";
            regex = "(insert.*into)(.+)(values)(.+)";
            tables = getMatchedPosition(regex, sql, 2);
            conditions = getMatchedPosition(regex, sql, 4);

        } else if (sql.indexOf("update") == 0) {

            type = "update";
            regex = "(update)(.+)(set)(.+)(where*)";
            tables = getMatchedPosition(regex, sql, 2);
            conditions = getMatchedPosition(regex, sql, 4);
            where = getWhere(sql);

        } else if (sql.indexOf("delete") == 0) {

            type = "delete";
            if (isContains(sql, "\\s+where\\s+")) {
                regex = "(delete.*from)(.+)(where)(.+)";
                where = getMatchedPosition(regex, sql, 4);
            } else {
                regex = "(delete.*from)(.+)($)";
            }
            tables = getMatchedPosition(regex, sql, 2);
        }
    }

    // get matched string from position.
    private static String getMatchedPosition(String regex, String text, int position)
    {
        try {
            // ignore string case.
            Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE);
            Matcher matcher = pattern.matcher(text);
            while (matcher.find()) {
                return matcher.group(position).trim();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // get where condition.
    private static String getWhere(String sql)
    {
        String regex = "";

        if (isContains(sql, "where\\s+")) {
            if (isContains(sql, "order\\s+by")) {
                regex = "(where\\s+)(.+)(order)";
            } else {
                regex = "(where\\s+)(.+)($)";
            }
        } else {
            return null;
        }

        return getMatchedPosition(regex, sql, 2).trim();
    }

    // get order by content.
    private static String getOrderIndex(String sql)
    {
        String regex = "";

        if (isContains(sql, "order\\s+by")) {
            regex = "(order\\s+by)(.+)($)";
        } else {
            return null;
        }

        return getMatchedPosition(regex, sql, 2).trim();
    }

    // check if contains.
    private static boolean isContains(String lineText, String word)
    {
        // ignore string case.
        Pattern pattern = Pattern.compile(word, Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(lineText);
        return matcher.find();
    }

    public static void main(String[] args)
    {
        SqlParser.parse("select * from ADMIN_MSTR order by ID desc");
        System.out.println("tables: " + tables + ", condition: " + conditions);

        SqlParser.parse("select AM_NO, AM_PASSWD from ADMIN_MSTR where ID=3");
        System.out.println("tables: " + tables + ", condition: " + conditions + ", where: " + where);

        SqlParser.parse("select AM_NO, AM_PASSWD from ADMIN_MSTR where ID>0 order by ID desc");
        System.out.println("tables: " + tables + ", condition: " + conditions + ", where: " + where + ", order by: " + order);

        SqlParser.parse("insert into ADMIN_MSTR(ID, NAME, PASSWD) values ('1', 'Sam', 'abcd1234')");
        System.out.println("tables: " + tables + ", condition: " + conditions);

        SqlParser.parse("update ADMIN_MSTR set NAME='ABCD', PASSWD='123465' where ID=1");
        System.out.println("tables: " + tables + ", condition: " + conditions + ", where: " + where);

        SqlParser.parse("delete from ADMIN_MSTR where ID=2");
        System.out.println("tables: " + tables + ", where: " + where);
    }
}

2008年12月2日 星期二

百谷虎 BaiGooHoo 搜尋網站, 最強的搜尋網??


如果說美國人最有創意, 日本人做事最有規劃, 那中國人的強項一定少不了模仿。
看看這個百谷虎BaiGooHoo搜尋網站, 商標結合了三大搜尋廠特色:

再看看搜尋結果:

虧他們想得出來用三個分頁顯示...昏倒

這...應該也算是有創意吧!!笑到肚子痛^^

2008年12月1日 星期一

擁有各式各樣桌面圖片 -- DeskCity.com

如果你也像我一樣是個長時間處在電腦前面的工作者, 那麼有一個舒服的桌面非常重要。
DeskCity.com 的桌面全部都是高解析度, 不用擔心螢幕過大鉅齒狀化問題, 也有專門寬螢幕的桌面。
希望對大家有幫助^^