XSBuilder.java

import java.text.*; // for NumberFormat
import java.util.regex.*;

// Note workaround for JDK 1.4.0 in XStream constructor.
import com.thoughtworks.xstream.*;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.converters.reflection.
    PureJavaReflectionProvider;


/**
Construct or parse XML-formatted network messages.  Message format
includes leading length field.  Formatting/parsing use the XStream 
library (http://xstream.codehaus.org/).
*/
public class XSBuilder implements MsgBuilder
{
    protected XStream xstream;
    protected Pattern msgHeaderPat;


    public XSBuilder()
    {
        // An XML/object parser...
        xstream =
            new XStream(
                new PureJavaReflectionProvider(),
                new DomDriver());
        // Recognizes a message header.
        msgHeaderPat = Pattern.compile("<<\\d\\d\\d\\d\\d\\d\\d>>");
    }


    public String makeMessageFromObject(Object obj)
    {
        String msg = xstream.toXML(obj);
        NumberFormat f = NumberFormat.getIntegerInstance();
        // Hack to get character count with leading zeroes.
        String nstr = "0000000" + f.format(msg.length());
        int lstr = nstr.length();
        return "<<" + nstr.substring(lstr-7) + ">>" + msg;
    }


    public Object getMessageFromString(StringBuffer sbuf)
    {
        if (sbuf.length() < 12)
            return null;

        Matcher m = msgHeaderPat.matcher(sbuf);
        if (m.find()) {
            int mlen = m.end() - m.start() + 1;
            String s = sbuf.substring(m.start()+2,mlen-3);
            int msglen = Integer.parseInt(s);
            if (sbuf.length() >= m.start() + msglen + 11) {
                String msg =
                    sbuf.substring(m.start()+11,m.start()+11+msglen);
                sbuf.delete(0,m.start()+11+msglen);
                Object o = xstream.fromXML(msg);
                return o;
                }
            }

        return null;
    }
}