Archives: 2008 October
  • JAVA字符串分解方法

    October 22nd, 2008



      大多数Java程序员都曾经使用过java.util.StringTokenizer类。它是一个很方便的字符串分解器,主要用来根据分隔符把字符串分割成标记(Token),然后按照请求返回各个标记。这个过程称为Tokenization,实际上就是把字符序列转换成应用程序能够理解的多个标记。

      虽然StringTokenizer用起来很方便,但它的功能却很有限。这个类只是简单地在输入字符串中查找分隔符,一旦找到了分隔符就分割字符串。它不会检查分隔符是否在子串之中这类条件,当输入字符串中出现两个连续的分隔符时,它也不会返回”"(字符串长度为0)形式的标记。

      为了突破这些局限,Java 2平台提供了BreakIterator类,它是在StringTokenizer之上改进的字符串分解器。由于JDK 1.1.x没有提供这个类,为了满足自己的需要,开发者经常花费很多时间从头开始编写分解器。在涉及到数据格式化处理的大型工程中,这类定制的字符串分解器有时随处可见,而且这种情况并不罕见。

      本文的目标是帮助你利用现有的StringTokenizer类,编写一个高级字符串分解器。

    二、StringTokenizer的局限

      你可以用以下三种构造函数中的任意一种创建StringTokenizer分解器:

    StringTokenizer(String sInput):以空白字符(“ ”,“\t”,“\n”)为分隔符分割字符串。
    StringTokenizer(String sInput, String sDelimiter):以sDelimiter为分隔符分割字符串。
    StringTokenizer(String sInput, String sDelimiter, boolean bReturnTokens):以sDelimiter为分隔符分割字符串,但如果bReturnTokens为true,则分隔符也作为标记返回。
      第一个构造函数不检查输入字符串是否包含子串。例如,如果以空白字符为分隔符分割“hello. Today \”I am \” going to my home town”,则字符串分解结果是hello.、Today、”I、am、”、going等,而不是hello.、Today、”I am “、going等。

      第二个构造函数不检查两个分隔符连续出现的情况。例如,如果以“,”为分隔符分割“book, author, publication,,,date published”这个字符串,则StringTokenizer返回book、author、publication和date published这四个标记,而不是book、author、publication、”"、”"和date published这6个标记(其中”"表示0长度字符串)。要得到6个标记的答案,你必须把StringTokenizer的bReturnTokens参数设置为true。

      允许设置值为true的bReturnTokens参数是一个重要的功能,因为它考虑到了分隔符连续出现的情况。例如,使用第二个构造函数时,如果数据是动态收集得到而且要用来更新数据库中的表,输入字符串中的标记对应着表里面列的值,那么当我们不能确定哪一个列应该设置为”"时,我们就无法把输入串中的标记映射到数据库列。假设我们要把记录插入到一个有6个列的表,而输入数据中包含两个连续的分隔符。此时,StringTokenizer的分解结果是5个标记(两个连续的分隔符代表”"标记,它将被StringTokenizer忽略),而我们却有6个字段需要设置。同时,我们也不知道连续分隔符在哪里出现,所以也就不知道哪一个列应该设置成”"。

      当标记本身等同于分隔符(无论是长度还是值)且位于子串之内时,第三个构造函数无效。例如,如果我们要以“,”为分隔符分解字符串“book, author, publication,\”,\”,date published”(这个字符串包含一个“,”标记,它与分隔符一样),结果是book、author、publication、”、”、date published这六个标记,而不是book、author、publication、,(逗号字符)、date published这五个标记。再提醒一下,即使我们把StringTokenizer的bReturnTokens参数设置设置成了true,在这种情况下也没有什么帮助。

    三、高级字符串分解器

      在编写代码之前,你必须搞清楚一个好的分解器有哪些基本要求。因为Java开发者已经习惯于使用StringTokenizer类,所以一个好的分解器应该提供StringTokenizer类提供的所有实用方法,比如hasMoreTokens()、nextToken()、countTokens()。

      本文提供的代码很简单,而且大部分代码足以自我解释。在这里,我主要利用了StringTokenizer类(创建类实例时bReturnTokens参数设置为true),并提供了上面提到的几个方法。大多数时候标记与分隔符不同,有些时候分隔符却要作为标记输出(尽管非常罕见),此时如果出现了对标记的请求,分解器要把分隔符作为标记输出。创建PowerfulTokenizer对象时,你只需要提供输入字符串和分隔符这两个参数,PowerfulTokenizer将在内部使用bReturnTokens设置成true的StringTokenizer。(这么做的原因在于,如果不是用bReturnTokens设置成true的方式创建StringTokenizer,那么它将在解决先前提出的问题时受到限制)。为了正确地控制分解器,代码在几个地方(计算标记的总数量以及nextToken())检查bReturnTokens是否设置成了true。

      你可能已经发现,PowerfulTokenizer实现了Enumeration接口,从而也就实现了hasMoreElements()和nextElement()这两个方法,而这两个方法又分别把调用直接委托给hasMoreTokens()和nextToken()。(由于实现了Enumeration接口,PowerfulTokenizer实现了与StringTokenizer的向后兼容。)

      我们来看一个例子,假设输入字符串是“hello, Today,,, \”I, am \”, going to,,, \”buy, a, book\””,分隔符是“,”。用分解器分割这个字符串时返回结果如表1所示:

    表1:字符串分解结果

      输入字符串包含11个逗号(,)字符,其中3个在子串里面、4个连续出现(“Today,,,”中包含两个连续逗号,第一个逗号是Today的分隔符)。下面是PowerfulTokenizer计算标记总数的算法:

    如果bReturnTokens=true,把子串中的分隔符数量乘以2,再从实际总数量减去该数字,就得到了标记的总数。理由是,对于子串“buy, a, book”,StringTokenizer将返回5个标记(即“buy:,:a:,:book”),而PowerfulTokenizer将返回一个标记(即“buy, a, book”),两者的差值是4(即,2乘以子串中的分隔符数量)。这个公式对于所有包含分隔符的子串都有效。
    类似地,对于bReturnTokens=false的情形,我们从实际总数(19)减去表达式[分隔符总数(11)- 连续分隔符数量(4) + 子串中的分隔符数量(3)]。由于这时我们不返回分隔符,它们(非连续出现或在子串内部)对我们来说没有用,上面的公式为我们返回了标记的总数量(9)。
      请记住这两个公式,它们是PowerfulTokenizer的核心。这两个公式适用于几乎所有它们各自条件下的情形。但是,如果你有更复杂的要求,不能使用这两个公式,那么你应该在编写代码之前分析各种可能出现的情况,并设计出自己的公式。

    // 检查分隔符是否位于子串之内
    for (int i=1; i<aiIndex.length; i++)>/td>
    {
    iIndex = sInput.indexOf(sDelim, iIndex+1);
    if (iIndex == -1)
    break;
    // 如果分隔符位于子串之内,则向前分析直至子串结束
    while (sInput.substring(iIndex-iLen, iIndex).equals(sDelim))
    {
    iNextIndex = sInput.indexOf(sDelim, iIndex+1);
    if (iNextIndex == -1)
    break;
    iIndex = iNextIndex;
    }
    aiIndex[i] = iIndex;
    //System.out.println(”aiIndex[" + i + "] = ” + iIndex);
    if (isWithinQuotes(iIndex))
    {
    if (bIncludeDelim)
    iTokens -= 2;
    else
    iTokens -= 1;
    }
    }

      countTokens()方法检查子串是否包含双引号。如果包含,那么它减少总数并把索引值修改为字符串中下一个双引号出现的位置(如上面的代码片断所示)。如果bReturnTokens是false,那么它从总数减去输入字符串中出现的非连续分隔符的数量。

    // 如发现多个连续的分隔符,则返回”"作为标记
    if ( (sPrevToken.equals(sDelim)) && (sToken.equals(sDelim)) ) {
    sPrevToken = sToken;
    iTokenNo++;
    return “”;
    }

    // 检查标记本身是否等于分隔符
    if ( (sToken.trim().startsWith(”\”")) && (sToken.length() == 1) ) {
    // 标记本身等于分隔符的特殊情况
    String sNextToken = oTokenizer.nextToken();
    while (!sNextToken
    sToken += sNextToken;
    sPrevToken = sToken;
    iTokenNo++;
    return sToken.substring(1, sToken.length()-1);
    }
    // 检查字符串中是否包含子串
    else if ( (sToken.trim().startsWith(”\”"))
    && (!((sToken.trim().endsWith(”\”"))
    && (!sToken.trim().endsWith(”\”\”")))) )
    {
    if (oTokenizer.hasMoreTokens())
    {
    String sNextToken = oTokenizer.nextToken();
    // 检查”\”\”"
    while (!((sNextToken.trim().endsWith(”\”"))
    && (!sNextToken.trim().endsWith(”\”\”"))) )
    {
    sToken += sNextToken;
    if (!oTokenizer.hasMoreTokens())
    {
    sNextToken = “”;
    break;
    }
    sNextToken = oTokenizer.nextToken();
    }
    sToken += sNextToken;
    }
    }
      nextToken()方法通过StringTokenizer.nextToken方法获取标记,并检查标记中的双引号字符。如果发现了这些字符,它继续获取标记直至不能再找到带有双引号的标记。另外,它还把标记保存到一个变量(sPrevToken,参见本文后面完整的源代码)以检查连续出现的分隔符。如果nextToken()发现等同于分隔符的连续多个标记,那么它返回”"(长度为0的字符串)作为标记。

      按照类似的方法,hasMoreTokens()方法检查已经返回的标记数量是否小于标记的总数量。

  • 比JUnit4更酷的Annotation

    October 22nd, 2008

    如果你看过p-unit如何测试Vector#add(Object)是线程安全的,就可以体会到p-unit如何适合多线程测试。
    如果你觉得check_testAdd不符合你的编程风格的话,现在你可以使用 Annotation来表示check方法了,这就是@checkMethod Annotation, 上一篇文中的例子将写成:
    public class VectorTest extends TestCase {
        public static void main() {
           ConcurrentRunner runner = new ConcurrentRunner();
           runner.setMethodFilter(new AnnotationMethodFilter());
           runner.run(VectorTest.class);
        }
        private Vector _vector = new Vector();
      
        @Test(checkMethod = “checkAdd”)
        public void testAdd() {
            _vector.add(new Object());
        }

        public void checkAdd() {
            assertEquals(10, _vector.size());
        }
    }
    上面例子中@Test表示该方法为测试案例,checkMethod表示该方法对应的检查方法,这在某种程度上帮助你将测试逻辑和断言(assert部分) 分离。 p-unit 核心部分支持jdk >= 1.3 因此内建标准的method filter还是根据test的命名规则,test开头以及check_前缀做为该test的check方法。为了使用Annotation,只需加入下面一行代码:
           runner.setMethodFilter(new AnnotationMethodFilter());
    这也使得你可以定制自己的命名规则,嵌入p-unit内核。体会到p-unit酷的部分了吗?

    另外,p-unit支持@Test(expected=Throwable.class),这点和JUnit 4完全一样。
    该功能不包含在p-unit-0.10 release中,将在下一个版本中包含该功能,或者你可以check out svn最新代码来抢先体验该功能。

  • Restlet之AJAX实践

    October 22nd, 2008

    在下面的实践性指南中,我将以一个简单的“微型blog”(microblog)为例详细说明在Restlet中如何使用AJAX技术。

    预备知识:

    在编写代码之前,让我们先来回顾一下有关restlet的知识(如果你不曾了解或者忘记的话):

             * RESTful是什么?
             * 了解Restlet
             * 如何使用Restlet?        
             * Restlet指南
             * 如何使用db4o简化持久化数据存储?
             * 如何在Prototype.js中使用JSON?

    微型blog结构:

        * Web client:web浏览器通过JSON协议以RESTful的方式((GET/PUT/POST/DELETE))调用后台服务;
        * 服务器端:利用db4o作为数据存储服务提供者,并通过Restlet将数据暴露给web服务;
        * 服务端处理过程:在Restlet中,Application接收到Request后将其分配给相应的Router,Router寻找相匹配的Resource类并实例化匹配好的Resource类、将Request传送给它,Resource类对象处理Request并返回相应的表达法(representation)。

    DB4OSimpler.Class:

    这是一个非常简单的db4o功能封装类,它的generalOperate方法囊括了一些常见的数据操作。

    请注意:这个类只是能工作于非并发访问模式,因为它只使用OpenFile的方法简单地打开数据文件进行读写,而这在并发访问时会遇到致命的共享文件锁错误。如果需要处理并发访问的话,请将其修改为Client/Server模式:将Db4o.openFile修改为Db4o.openServer即可。

        package com.bjinfotech.util;
    
        import com.db4o.Db4o;
        import com.db4o.ObjectContainer;
        import com.db4o.query.Query;
        import java.util.ArrayList;
        import java.util.Iterator;
        import java.util.List;
        import org.apache.commons.beanutils.*;
        /**
         * It's very clean from its name that it works as db4o function simpler.
         * Its generalOperate method handles general operation with db4o.
         * @author cleverpig
         *
         */
        public class DB4OSimpler {
            //operation constants that will be used as generalOperate method's param
            public static final int OPERATION_SAVE=0;
            public static final int OPERATION_LOAD=1;
            public static final int OPERATION_UPDATE=2;
            public static final int OPERATION_DELETE=3;
            public static final int OPERATION_QUERY=4;
            public static final int OPERATION_LIST=5;
            public static final int OPERATION_CLEAR=6;
            /**
             * perform general Operation
             * @param fileName
             * @param op_code corresponding integer type constant
             * @param example object which is needed in operation
             * @param keyFieldName object's key field name
             * @return
             */
            public static Object generalOperate(
                    String fileName,
                    int op_code,
                    Object example,
                    String keyFieldName){
                Object ret=null;
                //open db4o file to get ObjectContainer
                ObjectContainer db=Db4o.openFile(fileName);
                Iterator iter=null;
                List list=null;
                Query query=null;
                try{
                    //perform operation according to op_code param value
                    switch(op_code){
                    case OPERATION_SAVE:
                        //just set!It's very simple!
                        db.set(example);
                        ret=example;
                        break;
                    case OPERATION_LOAD:
                        //just get!
                        list=db.get(example);
                        if (list!=null && list.size()>0){
                            ret=list.get(0);
                        }
                        break;
                    case OPERATION_UPDATE:
                        //at first,I find objects which will be updated with its key field value
                        query=db.query();
                        query.constrain(example.getClass());
                        query.descend(keyFieldName)
                            .constrain(BeanUtils.getProperty(example, keyFieldName));
                        iter=query.execute().listIterator();
                        //and then delete all of them
                        while(iter.hasNext()){
                            db.delete(iter.next());
                        }
                        //set new one,now!
                        db.set(example);
                        ret=example;
                        break;
                    case OPERATION_DELETE:
                        //just like update process:find firstly,and then delete them
                        query=db.query();
                        query.constrain(example.getClass());
                        query.descend(keyFieldName)
                            .constrain(BeanUtils.getProperty(example, keyFieldName));
                        iter=query.execute().listIterator();
                        if (iter.hasNext()){
                            while(iter.hasNext()){
                                db.delete(iter.next());
                            }
                            ret=true;
                        }
                        else{
                            ret=false;
                        }
                        break;
                    case OPERATION_QUERY:
                        //just like update process:find firstly,and then return them
                        query=db.query();
                        query.constrain(example.getClass());
                        query.descend(keyFieldName)
                            .constrain(BeanUtils.getProperty(example, keyFieldName));
                        iter=query.execute().listIterator();
                        list=new ArrayList();
                        while(iter.hasNext()){
                                list.add(iter.next());
                        }
                        if (list.size()>0)
                            ret=list;
                        break;
                    case OPERATION_LIST:
                        //return list of object which class is example.class.
                        list=new ArrayList();
                        iter=db.query(example.getClass()).listIterator();
                        while(iter.hasNext()){
                            list.add(iter.next());
                        }
                        if (list.size()>0)
                            ret=list;
                        break;
                    case OPERATION_CLEAR:
                        //delete anything which class is example.class.
                        iter=db.query(example.getClass()).listIterator();
                        int deleteCount=0;
                        while(iter.hasNext()){
                            db.delete(iter.next());
                            deleteCount++;
                        };
                        ret=true;
                        break;
                    }
                    //commit,finally
                    db.commit();
                }
                catch(Exception ex){
                    db.rollback();
                    ex.printStackTrace();
                }
                finally{
                    db.close();
                }
                return ret;
            }
        }
    

    MicroblogApplication.Class:

    这是REST风格的微型blog服务端代码,它提供了静态文件访问、资源(MicroblogResource这个Resource类)的访问服务:

    请注意:在这里我使用了TunnelService来代替了自定义finder的方式,因为我认为TunnelService非常易用并且减少开发时间。但我也将在本文后面讨论如何使用自定义finder的方式来实现相同功能。

        package com.bjinfotech.restlet.practice.demo.microblog;
    
        import org.restlet.Application;
        import org.restlet.Component;
        import org.restlet.Directory;
        import org.restlet.Restlet;
        import org.restlet.Router;
        import org.restlet.data.Protocol;
    
        /**
         * restful server
         * it serves static files and resource
         * @author cleverpig
         *
         */
        public class MicroblogApplication {
            public static void main(String[] argv) throws Exception{
                Component component=new Component();
                //add http protocol
                component.getServers().add(Protocol.HTTP,8182);
                //add file protocol for accessing static web files in some directories
                component.getClients().add(Protocol.FILE);
    
                Application application=new Application(component.getContext()){
                    @Override
                    public Restlet createRoot(){
                        //directory where static web files live
                        final String DIR_ROOT_URI=”file:///E:/eclipse3.1RC3/workspace/RestletPractice/static_files/”;
                        //create router
                        Router router=new Router(getContext());
                        //attach static web files to “www” folder
                        Directory dir=new Directory(getContext(),DIR_ROOT_URI);
                        dir.setListingAllowed(true);
                        dir.setDeeplyAccessible(true);
                        dir.setNegotiateContent(true);
                        router.attach(”/www/”,dir);
                        //attach resource class:MicroblogResource to “/restful/blog” as web service URI
                        router.attach(”/restful/blog”,MicroblogResource.class);
                        return router;
                    }
                };
                //use TunnelService to simplify request’s dispatching  
                application.getTunnelService().setEnabled(true);
                application.getTunnelService().setMethodTunnel(true);
                application.getTunnelService().setMethodParameter(”method”);
                //attach application
                component.getDefaultHost().attach(application);
                component.start();
            }
        }
    

    microblogAppInterface.js:

    这是用在microblog.html中的JavaScript文件,它以REST的风格调用在服务端暴露的服务:

        var SAVE_MODEL=1;
        var UPDATE_MODEL=2;
    
        function switchEditorModel(model){
            switch(model){
                case SAVE_MODEL:
                    Element.show('save_button');
                    Element.hide('update_button');
                    Element.hide('remove_button');
                    Element.hide('new_button');
                    break;
                case UPDATE_MODEL:
                    Element.hide('save_button');
                    Element.show('update_button');
                    Element.show('remove_button');
                    Element.show('new_button');
                    break;
            }
        }
    
        ...
    
        Event.observe(
            'save_button',
            'click',
            function(){
                var formObj=Form.serialize('edit_form',true);
                var xmlHttp = new Ajax.Request(
                    "/restful/blog?method=PUT",
                    {
                        method: 'post',
                        parameters: 'json='+encodeURIComponent(Object.toJSON(formObj)),
                        onComplete: function(transport){
                            var retObj=transport.responseText.evalJSON();
                            if (retObj.subject){
                                alert('ok,"'+retObj.subject+'" was saved!');
                                refreshBloglist();
                                switchEditorModel(UPDATE_MODEL);
                            }
                        }
                    }
                );
            },
            false
        );
    
        ...
    
        function refreshBloglist(){
            var xmlHttp = new Ajax.Request(
                "/restful/blog",
                {
                    method: 'get',
                    parameters: '',
                    onComplete: function(transport){
                        var retObjs=transport.responseText.evalJSON();
                        if (retObjs.length && retObjs.length>0){
                            var listRepr='<ul>\n';
                            retObjs.each(function(obj,index){
                                if (index<retObjs.length-1)
                                    listRepr+='<li>'+
                                        '<a href="javascript:load(\''+obj.subject+'\');">'+obj.subject+'</a></li>\n';
                            });
                            listRepr+="<\ul>\n";
                            $('blogList').innerHTML=listRepr;
                        }
                        else{
                            $('blogList').innerHTML='Here is empty';
                        }
                    }
                }
            );
        }
        function load(subject){
            var xmlHttp = new Ajax.Request(
                    "/restful/blog",
                    {
                        method: 'get',
                        parameters: 'subject='+encodeURIComponent(subject),
                        onComplete: function(transport){
                            var retObj=transport.responseText.evalJSON();
                            if (retObj.subject){
                                $('subject').value=retObj.subject;
                                $('content').value=retObj.content;
                                $('tags').value=retObj.tags;
                                alert('ok,"'+retObj.subject+'" was loaded!');
                                switchEditorModel(UPDATE_MODEL);
                            }
                        }
                    }
                );
        }
    

    MicroblogResource.Class:

    这是微型blog资源类。

        package com.bjinfotech.restlet.practice.demo.microblog;
    
        import java.util.List;
        import java.util.logging.Logger;
    
        import org.restlet.Context;
        import org.restlet.data.CharacterSet;
        import org.restlet.data.Form;
        import org.restlet.data.Language;
        import org.restlet.data.MediaType;
        import org.restlet.data.Request;
        import org.restlet.data.Response;
        import org.restlet.resource.Representation;
        import org.restlet.resource.Resource;
        import org.restlet.resource.ResourceException;
        import org.restlet.resource.StringRepresentation;
        import org.restlet.resource.Variant;
        import org.restlet.data.Status;
        import com.bjinfotech.util.JSONSimpler;
        import com.bjinfotech.util.Utils;
    
        public class MicroblogResource extends Resource {
            Logger log=Logger.getLogger(MicroblogResource.class.getSimpleName());
            //MicroblogPersistenceManager
            MicroblogPersistenceManager micoblogPM=new MicroblogPersistenceManager();
            //StringRepresentation constant
            final StringRepresentation NO_FOUND_REPR=new StringRepresentation("No found!");
            final StringRepresentation ERR_REPR=new StringRepresentation("something wrong!");
            //json param name in request
            final String JSON_PARAM="json";
    
            public MicroblogResource(
                    Context context,
                    Request request,
                    Response response) {
                super(context, request, response);
                this.getVariants().add(new Variant(MediaType.TEXT_PLAIN));
                //it's important,please don't forget it.
                this.setAvailable(true);
                this.setModifiable(true);
                this.setNegotiateContent(true);
            }
            @Override
            /**
             * representing after calling default get handle
             * @param variant
             */
            public Representation represent(Variant variant) throws ResourceException{
                log.info("representing after calling default get handle...");
                Representation result = null;
                if (variant.getMediaType().equals(MediaType.TEXT_PLAIN)) {
                    //find "subject" param in request,"subject" param is tranformed from web client.it means load one blog with special subject
                    String subject=getRequest().getResourceRef().getQueryAsForm().getFirstValue("subject");
                    log.info("subject:"+subject);
                    //handle query
                    if (subject!=null && subject.length()>0){
                        //find blog with special subject
                        Microblog example=new Microblog();
                        example.setSubject(subject);
                        List queryRet=micoblogPM.query(example);
                        //return result in JSON format StringRepresentation
                        if (queryRet!=null && queryRet.size()>0){
                            result=new StringRepresentation(
                                    JSONSimpler.serializeFromBean(queryRet.get(0)),
                                    MediaType.APPLICATION_JSON,
                                    Language.ALL,
                                    CharacterSet.UTF_8
                                );
                        }
                        else{
                            result=NO_FOUND_REPR;
                        }
                    }
                    else{
                        //return blog list in JSON format StringRepresentation
                        result=new StringRepresentation(
                                JSONSimpler.serializeFromBeanList(micoblogPM.list()),
                                MediaType.APPLICATION_JSON,
                                Language.ALL,
                                CharacterSet.UTF_8
                            );
                    }
                }
                return result;
            }
            /**
             * call MicroblogPersistenceManager's method excluding query and list,just save/update/delete.
             * @param method method name
             * @param jsonParamVal json param value coming from request
             * @return
             */
            protected StringRepresentation callMethod(String method,String jsonParamVal){
                //transform json format string to Microblog object
                Microblog microblog=(Microblog)JSONSimpler.deserializeToBean(jsonParamVal,Microblog.class);
                if (microblog!=null){
                    //call method and gain json format string as responseText
                    String responseText=Utils.callMethodAndGainResponseJSONStr(
                            micoblogPM,
                            method,
                            jsonParamVal,
                            Microblog.class);
                    log.info("response:"+responseText);
                    //return json StringRepresentation
                    return new StringRepresentation(
                            responseText,
                            MediaType.APPLICATION_JSON,
                            Language.ALL,
                            CharacterSet.UTF_8
                    );
                }
                else{
                    return ERR_REPR;
                }
            }
            @Override
            /**
             * handling post in high level
             * @param entity
             */
            public void acceptRepresentation(Representation entity) throws ResourceException{
                log.info("handling post in high level...");
                super.acceptRepresentation(entity);
                getResponse().setStatus(Status.SUCCESS_OK);
                Form f = new Form(entity);
                String jsonParamVal=f.getValues(JSON_PARAM);
                log.info("json param:"+jsonParamVal);
                //call update and set response
                getResponse().setEntity(callMethod("update",jsonParamVal));
            }
            @Override
            /**
             * handling put in high level
             * @param entity
             */
            public void storeRepresentation(Representation entity) throws ResourceException{
                log.info("handling put in high level...");
                super.storeRepresentation(entity);
                getResponse().setStatus(Status.SUCCESS_CREATED);
                Form f = new Form(entity);
                String jsonParamVal=f.getValues(JSON_PARAM);
                log.info("json param:"+jsonParamVal);
                //call save and set response
                getResponse().setEntity(callMethod("save",jsonParamVal));
            }
            @Override
            /**
             * handling delete in high level
             * @param entity
             */
            public void removeRepresentations() throws ResourceException{
                log.info("handling delete in high level...");
                super.removeRepresentations();
                getResponse().setStatus(Status.SUCCESS_OK);
                Form f = getRequest().getEntityAsForm();
                String jsonParamVal=f.getValues(JSON_PARAM);
                log.info("json param:"+jsonParamVal);
                //call delete and set response
                getResponse().setEntity(callMethod("delete",jsonParamVal));
            }
        }
    

    Microblog.Class:

    这是描述微型blog对象结构的Microblog.Class。

        package com.bjinfotech.restlet.practice.demo.microblog;
    
        public class Microblog {
            private String subject;
            private String content;
            private String tags;
    
            public String getSubject() {
                return subject;
            }
            ...
    
        }
    

     

  • Google AJAX Language API开发者参考

    October 22nd, 2008

    使用AJAX Language API,你可以仅使用JavaScript来完成对某个网页上某个区域的语言进行翻译和检测的工作。这个API是新开发的,因此相对于一个完美的文档来说,可能有一些bug和微小的不足。我们会修补这些漏洞,因此请谅解、你可以加入AJAX APIs开发者论坛来给我们反馈和讨论这个API。

    面向读者

    本文档是面向对JavaScript编程和面向对象编程概念有所了解的人准备的。在互联网上有很多JavaScript教程

    介绍

    在Google Ajax Language API上的“Hello,World”程序

    开始学习本API的最简单方法是来看一个简单的例子,一下案例将检测给定语言,并且将其翻译为英文。

      <head>
        <script type="text/javascript" src="http://www.google.com/jsapi"></script>
        <script type="text/javascript">
    
        google.load("language", "1");
    
        function initialize() {
          var text = document.getElementById("text").innerHTML;
          google.language.detect(text, function(result) {
            if (!result.error && result.language) {
              google.language.translate(text, result.language, "en",
                                        function(result) {
                var translated = document.getElementById("translation");
                if (result.translation) {
                  translated.innerHTML = result.translation;
                }
              });
            }
          });
        }
        google.setOnLoadCallback(initialize);
    
        </script>
      </head>
      <body>
        <div id="text">你好,很高興見到你。</div>
        <div id="translation"></div>
      </body>
    

    你可以在这里查看该案例,并且随意修改和运行它。

    在你的网页中包含Ajax Language API

    为了在你的网页中加入AJAX Language API,你需要利用Google AJAX API Loader。该公有加载类允许你加载你所需要的所有AJAX API,包括这里的language API。你需要同时包含Google AJAX APIs的script标签并且调用google.load(”language”,”1″);。

    <script type="text/javascript" src="http://www.google.com/jsapi"></script>
    <script type="text/javascript">
      google.load("language", "1");
    </script>

    第一个script标签加载了google.load函数,这个函数允许你加载特定的 Google API。google.load(”language”,”1″)加载了Language API的第一版。当前,AJAX Language API的版本为1,但是不久后新版本将可以使用。参考下面的版本讨论来获得更多信息。

    API 更新

    google.load函数的第二个参数实际上是你所使用的AJAX Language API的版本。当前,AJAX Language API的版本为1,但是不久后新版本将可以使用。如果以后我们对API进行重要更新,我们将改变版本号,并且在Google CodeAJAX APIS 讨论中发布通知。当该事件发生后,我们预期会在至少一个月的时间内继续支持所有版本来使你有足够的时间来迁移你的代码。

    AJAX Language API团队对于最近日较的Bug修复和平台优化进行定期更新。这些Bug修复应当仅提高表现和修复Bug,但是我们有可能不小心破坏某些API用户、请使用AJAX APIs 讨论组来报告这样的问题。

    实例

    语言翻译

    本案例展示了一个简单的对一个JavaScript字符串变量进行翻译的过程。

    google.language.translate("Hello world", "en", "es", function(result) {
      if (!result.error) {
        var container = document.getElementById("translation");
        container.innerHTML = result.translation;
      }
    });

    查看案例(translate.html)

    语言检测

    这个案例展示了对于一个JavaScript字符串的语言检测。将返回语言代码。

    var text = "?Dónde está el ba?o?";
    google.language.detect(text, function(result) {
      if (!result.error) {
        var language = 'unknown';
        for (l in google.language.Languages) {
          if (google.language.Languages[l] == result.language) {
            language = l;
            break;
          }
        }
        var container = document.getElementById(”detection”);
        container.innerHTML = text + ” is: ” + language + “”;
      }
    });

    查看案例 (detection.html)

    翻译时源检测

    如下的案例类似于基本的翻译案例,但是它展示了如何在不知道源语言时对文本进行翻译。通过传入一个空字符串来表示未知的源语言,系统将在一次调用中自动检测和翻译。

    google.language.translate("Hello world", "", "es", function(result) {
      if (!result.error) {
        var container = document.getElementById("translation");
        container.innerHTML = result.translation;
      }
    });

    查看案例 (autotranslate.html)

    更多案例

    这里有进行交互作用的两个附加案例:

    第一个案例对预输入的文本字符串进行语言检测,同时也允许输入其他的文本。它同时也显示信任和可靠因数(confidence and reliability factors—译者注)。
    查看案例(detect.html)

    第二个附加案例进行翻译。同时他也允许与上述类似的交互作用。
    查看案例(translate.html)

    API细节

    支持的语言

    Google AJAX Language API现在支持下列语言。该技术正在不断的提高,同时我们的团队也在努力的扩展这个列表,因此请经常回来看看。你也可以访问Google Translate来查看最近更新的列表。

        * Arabic (阿拉伯语)
        * Chinese (Simplified and Traditional) (中文简体)
        * Dutch (荷兰语)
        * English (英语)
        * French (法语)
        * German (德语)
        * Greek (希腊语)
        * Italian (意大利语)
        * Japanese (日语)
        * Korean (韩语)
        * Portuguese (葡萄牙语)
        * Russian (俄语)
        * Spanish(西班牙语)

    支持的语言翻译对

    Google AJAX Language API现在支持下列语言翻译对。该技术正在不断的提高,同时我们的团队也在努力的扩展这个列表,因此请经常回来看看。你也可以访问Google Translate来查看最近更新的列表。

        * Arabic to English (阿拉伯语到英语)
        * Chinese to English (中文到英语)
        * Chinese (Simplified to Traditional) (简体中文到繁体中文)
        * Chinese (Traditional to Simplified) (繁体中文到简体中文)
        * Dutch to English (荷兰语到英语)
        * English to Arabic (英语到阿拉伯语)
        * English to Chinese (Simplified) (英语到简体中文)
        * English to Chinese (Traditional) (英语到繁体中文)
        * English to Dutch (英语到荷兰语)
        * English to French (英语到法语)
        * English to German (英语到德语)
        * English to Greek (英语到希腊语)
        * English to Italian (英语到意大利语)
        * English to Japanese (英语到日语)
        * English to Korean (英语到韩语)
        * English to Portuguese (英语到葡萄牙语)
        * English to Russian (英语到俄语)
        * English to Spanish (英语到西班牙语)
        * French to English (法语到英语)
        * French to German (法语到德语)
        * German to English (德语到英语)
        * German to French (德语到法语)
        * Greek to English (希腊语到英语)
        * Italian to English (意大利语到英语)
        * Japanese to English (日语到英语)
        * Korean to English (韩语到英语)
        * Portuguese to English (葡萄牙语到英语)
        * Russian to English (俄语到英语)
        * Spanish to English(西班牙语到英语)

    问题解决

    如果在你的代码中遇到了问题:

       1. 检查代码。请牢记JavaScript是对大小写敏感的语言。
       2. 使用JavaScript调试器、在Firefox中,你可以使用JavaScript控制台或者FireBug扩展。在IE中,你可以使用Microsoft Script Debugger。
       3. 在AJAX APIs讨论组中搜索。如果你找不到回答你问题的帖子,那么可以在讨论组中发布你的问题,并且包含指向你出问题的页面的链接。

  • JAVA使用通配符简化泛型使用

    October 22nd, 2008

    通配符是 Java™ 语言中最复杂的泛型之一,特别是围绕捕获通配符 的处理和令人困惑的错误消息。在这一期的 Java 理论与实践 中,资深 Java 开发人员 Brian Goetz 解释了一些由 javac 生成的怪异错误消息并提供了一些简化泛型使用的技巧和解决方法。
    自从泛型被添加到 JDK 5 语言以来,它一直都是一个颇具争议的话题。一部分人认为泛型简化了编程,扩展了类型系统从而使编译器能够检验类型安全;另外一些人认为泛型添加了很多不必要的复杂性。对于泛型我们都经历过一些痛苦的回忆,但毫无疑问通配符是最棘手的部分。


    通配符基本介绍


    泛型是一种表示类或方法行为对于未知类型的类型约束的方法,比如 “不管这个方法的参数 x 和 y 是哪种类型,它们必须是相同的类型”,“必须为这些方法提供同一类型的参数” 或者 “foo() 的返回值和 bar() 的参数是同一类型的”。


    通配符 — 使用一个奇怪的问号表示类型参数 — 是一种表示未知类型的类型约束的方法。通配符并不包含在最初的泛型设计中(起源于 Generic Java(GJ)项目),从形成 JSR 14 到发布其最终版本之间的五年多时间内完成设计过程并被添加到了泛型中。


    通配符在类型系统中具有重要的意义,它们为一个泛型类所指定的类型集合提供了一个有用的类型范围。对泛型类 ArrayList 而言,对于任意(引用)类型 T,ArrayList 类型是 ArrayList 的超类型(类似原始类型 ArrayList 和根类型 Object,但是这些超类型在执行类型推断方面不是很有用)。


     

  • 如何使用自定义Finder代替TunnelService

    October 21st, 2008

    修改Application:

        ...
        Router router = new Router(getContext());
        //It's very easy!
        router.setFinderClass(PrototypeFinder.class);
        ...
    

    自定义Finder:

        public class PrototypeFinder extends Finder {
               public  PrototypeFinder(Context context, Class<? extends Handler>
        targetClass) {
                       super(context, targetClass);
               }
    
               public void handle(Request request, Response response) {
                       //get  "_method" param value
                       Parameter p = request.getEntityAsForm().getFirst("_method");
                       //reset requst method accoring "_method" param value
                       request.setMethod(null != p ? Method.valueOf(p.getValue()) :
        request.getMethod());
                       super.handle(request, response);
               }
        }
    

    页面中的JavaScript代码片段:

        ...
    
        <script type="text/javascript" language="JavaScript">
        function callJSON() {
               new Ajax.Request('/ajax', {
                       parameters: 'name=PUT', method: 'put', putBody: "PUT BODY",
                       onComplete: function (transport) {
                               alert(transport.responseText);
                       }
               });
               new Ajax.Request('/ajax', {
                       parameters: 'name=POST', method: 'post',
                       onComplete: function (transport) {
                               alert(transport.responseText);
                       }
               });
               new Ajax.Request('/ajax', {
                       parameters: 'name=DELETE', method: 'delete',
                       onComplete: function (transport) {
                               alert(transport.responseText);
                       }
               });
        }
        </script>
    
        ...