auto import from //depot/cupcake/@135843
diff --git a/xml/src/main/java/org/kxml2/io/KXmlParser.java b/xml/src/main/java/org/kxml2/io/KXmlParser.java
new file mode 100644
index 0000000..0727bc7
--- /dev/null
+++ b/xml/src/main/java/org/kxml2/io/KXmlParser.java
@@ -0,0 +1,1440 @@
+/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The  above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE. */
+
+// Contributors: Paul Hackenberger (unterminated entity handling in relaxed mode)
+
+package org.kxml2.io;
+
+import java.io.*;
+import java.util.*;
+
+import org.xmlpull.v1.*;
+
+/** A simple, pull based XML parser. This classe replaces the kXML 1
+    XmlParser class and the corresponding event classes. */
+
+public class KXmlParser implements XmlPullParser {
+
+    private Object location;
+    static final private String UNEXPECTED_EOF = "Unexpected EOF";
+    static final private String ILLEGAL_TYPE = "Wrong event type";
+    static final private int LEGACY = 999;
+    static final private int XML_DECL = 998;
+
+    // general
+
+    private String version;
+    private Boolean standalone;
+
+    private boolean processNsp;
+    private boolean relaxed;
+    private Hashtable entityMap;
+    private int depth;
+    private String[] elementStack = new String[16];
+    private String[] nspStack = new String[8];
+    private int[] nspCounts = new int[4];
+
+    // source
+
+    private Reader reader;
+    private String encoding;
+    private char[] srcBuf;
+
+    private int srcPos;
+    private int srcCount;
+
+    private int line;
+    private int column;
+
+    // txtbuffer
+
+    private char[] txtBuf = new char[128];
+    private int txtPos;
+
+    // Event-related
+
+    private int type;
+    //private String text;
+    private boolean isWhitespace;
+    private String namespace;
+    private String prefix;
+    private String name;
+
+    private boolean degenerated;
+    private int attributeCount;
+    private String[] attributes = new String[16];
+    private int stackMismatch = 0;
+    private String error;
+
+    /** 
+     * A separate peek buffer seems simpler than managing
+     * wrap around in the first level read buffer */
+
+    private int[] peek = new int[2];
+    private int peekCount;
+    private boolean wasCR;
+
+    private boolean unresolved;
+    private boolean token;
+
+    public KXmlParser() {
+        // BEGIN android-changed
+        // We don't have a Runtime class at this time.
+        // srcBuf =
+        //         new char[Runtime.getRuntime().freeMemory() >= 1048576 ? 8192 : 128];
+        srcBuf = new char[8192];
+        // END android-changed
+    }
+
+    private final boolean isProp(String n1, boolean prop, String n2) {
+        if (!n1.startsWith("http://xmlpull.org/v1/doc/"))
+            return false;
+        if (prop)
+            return n1.substring(42).equals(n2);
+        else
+            return n1.substring(40).equals(n2);
+    }
+
+    private final boolean adjustNsp() throws XmlPullParserException {
+
+        boolean any = false;
+
+        for (int i = 0; i < attributeCount << 2; i += 4) {
+            // * 4 - 4; i >= 0; i -= 4) {
+
+            String attrName = attributes[i + 2];
+            int cut = attrName.indexOf(':');
+            String prefix;
+
+            if (cut != -1) {
+                prefix = attrName.substring(0, cut);
+                attrName = attrName.substring(cut + 1);
+            }
+            else if (attrName.equals("xmlns")) {
+                prefix = attrName;
+                attrName = null;
+            }
+            else
+                continue;
+
+            if (!prefix.equals("xmlns")) {
+                any = true;
+            }
+            else {
+                int j = (nspCounts[depth]++) << 1;
+
+                nspStack = ensureCapacity(nspStack, j + 2);
+                nspStack[j] = attrName;
+                nspStack[j + 1] = attributes[i + 3];
+
+                if (attrName != null && attributes[i + 3].equals(""))
+                    error("illegal empty namespace");
+
+                //  prefixMap = new PrefixMap (prefixMap, attrName, attr.getValue ());
+
+                //System.out.println (prefixMap);
+
+                System.arraycopy(
+                    attributes,
+                    i + 4,
+                    attributes,
+                    i,
+                    ((--attributeCount) << 2) - i);
+
+                i -= 4;
+            }
+        }
+
+        if (any) {
+            for (int i = (attributeCount << 2) - 4; i >= 0; i -= 4) {
+
+                String attrName = attributes[i + 2];
+                int cut = attrName.indexOf(':');
+
+                if (cut == 0 && !relaxed)
+                    throw new RuntimeException(
+                        "illegal attribute name: " + attrName + " at " + this);
+
+                else if (cut != -1) {
+                    String attrPrefix = attrName.substring(0, cut);
+
+                    attrName = attrName.substring(cut + 1);
+
+                    String attrNs = getNamespace(attrPrefix);
+
+                    if (attrNs == null && !relaxed)
+                        throw new RuntimeException(
+                            "Undefined Prefix: " + attrPrefix + " in " + this);
+
+                    attributes[i] = attrNs;
+                    attributes[i + 1] = attrPrefix;
+                    attributes[i + 2] = attrName;
+
+                    /*
+                                        if (!relaxed) {
+                                            for (int j = (attributeCount << 2) - 4; j > i; j -= 4)
+                                                if (attrName.equals(attributes[j + 2])
+                                                    && attrNs.equals(attributes[j]))
+                                                    exception(
+                                                        "Duplicate Attribute: {"
+                                                            + attrNs
+                                                            + "}"
+                                                            + attrName);
+                                        }
+                        */
+                }
+            }
+        }
+
+        int cut = name.indexOf(':');
+
+        if (cut == 0)
+            error("illegal tag name: " + name);
+
+        if (cut != -1) {
+            prefix = name.substring(0, cut);
+            name = name.substring(cut + 1);
+        }
+
+        this.namespace = getNamespace(prefix);
+
+        if (this.namespace == null) {
+            if (prefix != null)
+                error("undefined prefix: " + prefix);
+            this.namespace = NO_NAMESPACE;
+        }
+
+        return any;
+    }
+
+    private final String[] ensureCapacity(String[] arr, int required) {
+        if (arr.length >= required)
+            return arr;
+        String[] bigger = new String[required + 16];
+        System.arraycopy(arr, 0, bigger, 0, arr.length);
+        return bigger;
+    }
+
+    private final void error(String desc) throws XmlPullParserException {
+        if (relaxed) {
+            if (error == null)
+                error = "ERR: " + desc;
+        }
+        else
+            exception(desc);
+    }
+
+    private final void exception(String desc) throws XmlPullParserException {
+        throw new XmlPullParserException(
+            desc.length() < 100 ? desc : desc.substring(0, 100) + "\n",
+            this,
+            null);
+    }
+
+    /** 
+     * common base for next and nextToken. Clears the state, except from 
+     * txtPos and whitespace. Does not set the type variable */
+
+    private final void nextImpl() throws IOException, XmlPullParserException {
+
+        if (reader == null)
+            exception("No Input specified");
+
+        if (type == END_TAG)
+            depth--;
+
+        while (true) {
+            attributeCount = -1;
+
+            // degenerated needs to be handled before error because of possible
+            // processor expectations(!)
+
+            if (degenerated) {
+                degenerated = false;
+                type = END_TAG;
+                return;
+            }
+
+
+            if (error != null) {
+                for (int i = 0; i < error.length(); i++)
+                    push(error.charAt(i));
+                //                text = error;
+                error = null;
+                type = COMMENT;
+                return;
+            }
+
+
+            if (relaxed
+                && (stackMismatch > 0 || (peek(0) == -1 && depth > 0))) {
+                int sp = (depth - 1) << 2;
+                type = END_TAG;
+                namespace = elementStack[sp];
+                prefix = elementStack[sp + 1];
+                name = elementStack[sp + 2];
+                if (stackMismatch != 1)
+                    error = "missing end tag /" + name + " inserted";
+                if (stackMismatch > 0)
+                    stackMismatch--;
+                return;
+            }
+
+            prefix = null;
+            name = null;
+            namespace = null;
+            //            text = null;
+
+            type = peekType();
+
+            switch (type) {
+
+                case ENTITY_REF :
+                    pushEntity();
+                    return;
+
+                case START_TAG :
+                    parseStartTag(false);
+                    return;
+
+                case END_TAG :
+                    parseEndTag();
+                    return;
+
+                case END_DOCUMENT :
+                    return;
+
+                case TEXT :
+                    pushText('<', !token);
+                    if (depth == 0) {
+                        if (isWhitespace)
+                            type = IGNORABLE_WHITESPACE;
+                        // make exception switchable for instances.chg... !!!!
+                        //    else 
+                        //    exception ("text '"+getText ()+"' not allowed outside root element");
+                    }
+                    return;
+
+                default :
+                    type = parseLegacy(token);
+                    if (type != XML_DECL)
+                        return;
+            }
+        }
+    }
+
+    private final int parseLegacy(boolean push)
+        throws IOException, XmlPullParserException {
+
+        String req = "";
+        int term;
+        int result;
+        int prev = 0;
+
+        read(); // <
+        int c = read();
+
+        if (c == '?') {
+            if ((peek(0) == 'x' || peek(0) == 'X')
+                && (peek(1) == 'm' || peek(1) == 'M')) {
+
+                if (push) {
+                    push(peek(0));
+                    push(peek(1));
+                }
+                read();
+                read();
+
+                if ((peek(0) == 'l' || peek(0) == 'L') && peek(1) <= ' ') {
+
+                    if (line != 1 || column > 4)
+                        error("PI must not start with xml");
+
+                    parseStartTag(true);
+
+                    if (attributeCount < 1 || !"version".equals(attributes[2]))
+                        error("version expected");
+
+                    version = attributes[3];
+
+                    int pos = 1;
+
+                    if (pos < attributeCount
+                        && "encoding".equals(attributes[2 + 4])) {
+                        encoding = attributes[3 + 4];
+                        pos++;
+                    }
+
+                    if (pos < attributeCount
+                        && "standalone".equals(attributes[4 * pos + 2])) {
+                        String st = attributes[3 + 4 * pos];
+                        if ("yes".equals(st))
+                            standalone = new Boolean(true);
+                        else if ("no".equals(st))
+                            standalone = new Boolean(false);
+                        else
+                            error("illegal standalone value: " + st);
+                        pos++;
+                    }
+
+                    if (pos != attributeCount)
+                        error("illegal xmldecl");
+
+                    isWhitespace = true;
+                    txtPos = 0;
+
+                    return XML_DECL;
+                }
+            }
+
+            /*            int c0 = read ();
+                        int c1 = read ();
+                        int */
+
+            term = '?';
+            result = PROCESSING_INSTRUCTION;
+        }
+        else if (c == '!') {
+            if (peek(0) == '-') {
+                result = COMMENT;
+                req = "--";
+                term = '-';
+            }
+            else if (peek(0) == '[') {
+                result = CDSECT;
+                req = "[CDATA[";
+                term = ']';
+                push = true;
+            }
+            else {
+                result = DOCDECL;
+                req = "DOCTYPE";
+                term = -1;
+            }
+        }
+        else {
+            error("illegal: <" + c);
+            return COMMENT;
+        }
+
+        for (int i = 0; i < req.length(); i++)
+            read(req.charAt(i));
+
+        if (result == DOCDECL)
+            parseDoctype(push);
+        else {
+            while (true) {
+                c = read();
+                if (c == -1){
+                    error(UNEXPECTED_EOF);
+                    return COMMENT;
+                }
+
+                if (push)
+                    push(c);
+
+                if ((term == '?' || c == term)
+                    && peek(0) == term
+                    && peek(1) == '>')
+                    break;
+
+                prev = c;
+            }
+
+            if (term == '-' && prev == '-')
+                error("illegal comment delimiter: --->");
+
+            read();
+            read();
+
+            if (push && term != '?')
+                txtPos--;
+
+        }
+        return result;
+    }
+
+    /** precondition: &lt! consumed */
+
+    private final void parseDoctype(boolean push)
+        throws IOException, XmlPullParserException {
+
+        int nesting = 1;
+        boolean quoted = false;
+
+        // read();
+
+        while (true) {
+            int i = read();
+            switch (i) {
+
+                case -1 :
+                    error(UNEXPECTED_EOF);
+                    return;
+
+                case '\'' :
+                    quoted = !quoted;
+                    break;
+
+                case '<' :
+                    if (!quoted)
+                        nesting++;
+                    break;
+
+                case '>' :
+                    if (!quoted) {
+                        if ((--nesting) == 0)
+                            return;
+                    }
+                    break;
+            }
+            if (push)
+                push(i);
+        }
+    }
+
+    /* precondition: &lt;/ consumed */
+
+    private final void parseEndTag()
+        throws IOException, XmlPullParserException {
+
+        read(); // '<'
+        read(); // '/'
+        name = readName();
+        skip();
+        read('>');
+
+        int sp = (depth - 1) << 2;
+
+        if (depth == 0) {
+            error("element stack empty");
+            type = COMMENT;
+            return;
+        }
+
+        if (!name.equals(elementStack[sp + 3])) {
+            error("expected: /" + elementStack[sp + 3] + " read: " + name);
+
+            // become case insensitive in relaxed mode
+
+            int probe = sp;
+            while (probe >= 0 && !name.toLowerCase().equals(elementStack[probe + 3].toLowerCase())) {
+                stackMismatch++;
+                probe -= 4;
+            }
+
+            if (probe < 0) {
+                stackMismatch = 0;
+                //            text = "unexpected end tag ignored";
+                type = COMMENT;
+                return;
+            }
+        }
+
+        namespace = elementStack[sp];
+        prefix = elementStack[sp + 1];
+        name = elementStack[sp + 2];
+    }
+
+    private final int peekType() throws IOException {
+        switch (peek(0)) {
+            case -1 :
+                return END_DOCUMENT;
+            case '&' :
+                return ENTITY_REF;
+            case '<' :
+                switch (peek(1)) {
+                    case '/' :
+                        return END_TAG;
+                    case '?' :
+                    case '!' :
+                        return LEGACY;
+                    default :
+                        return START_TAG;
+                }
+            default :
+                return TEXT;
+        }
+    }
+
+    private final String get(int pos) {
+        return new String(txtBuf, pos, txtPos - pos);
+    }
+
+    /*
+    private final String pop (int pos) {
+    String result = new String (txtBuf, pos, txtPos - pos);
+    txtPos = pos;
+    return result;
+    }
+    */
+
+    private final void push(int c) {
+
+        isWhitespace &= c <= ' ';
+
+        if (txtPos == txtBuf.length) {
+            char[] bigger = new char[txtPos * 4 / 3 + 4];
+            System.arraycopy(txtBuf, 0, bigger, 0, txtPos);
+            txtBuf = bigger;
+        }
+
+        txtBuf[txtPos++] = (char) c;
+    }
+
+    /** Sets name and attributes */
+
+    private final void parseStartTag(boolean xmldecl)
+        throws IOException, XmlPullParserException {
+
+        if (!xmldecl)
+            read();
+        name = readName();
+        attributeCount = 0;
+
+        while (true) {
+            skip();
+
+            int c = peek(0);
+
+            if (xmldecl) {
+                if (c == '?') {
+                    read();
+                    read('>');
+                    return;
+                }
+            }
+            else {
+                if (c == '/') {
+                    degenerated = true;
+                    read();
+                    skip();
+                    read('>');
+                    break;
+                }
+
+                if (c == '>' && !xmldecl) {
+                    read();
+                    break;
+                }
+            }
+
+            if (c == -1) {
+                error(UNEXPECTED_EOF);
+                //type = COMMENT;
+                return;
+            }
+
+            String attrName = readName();
+
+            if (attrName.length() == 0) {
+                error("attr name expected");
+               //type = COMMENT;
+                break;
+            }
+
+            int i = (attributeCount++) << 2;
+
+            attributes = ensureCapacity(attributes, i + 4);
+
+            attributes[i++] = "";
+            attributes[i++] = null;
+            attributes[i++] = attrName;
+
+            skip();
+
+            if (peek(0) != '=') {
+                error("Attr.value missing f. "+attrName);
+                attributes[i] = "1";
+            }
+            else {
+                read('=');
+                skip();
+                int delimiter = peek(0);
+
+                if (delimiter != '\'' && delimiter != '"') {
+                    error("attr value delimiter missing!");
+                    delimiter = ' ';
+                }
+                else 
+                    read();
+                
+                int p = txtPos;
+                pushText(delimiter, true);
+
+                attributes[i] = get(p);
+                txtPos = p;
+
+                if (delimiter != ' ')
+                    read(); // skip endquote
+            }
+        }
+
+        int sp = depth++ << 2;
+
+        elementStack = ensureCapacity(elementStack, sp + 4);
+        elementStack[sp + 3] = name;
+
+        if (depth >= nspCounts.length) {
+            int[] bigger = new int[depth + 4];
+            System.arraycopy(nspCounts, 0, bigger, 0, nspCounts.length);
+            nspCounts = bigger;
+        }
+
+        nspCounts[depth] = nspCounts[depth - 1];
+
+        /*
+                if(!relaxed){
+                for (int i = attributeCount - 1; i > 0; i--) {
+                    for (int j = 0; j < i; j++) {
+                        if (getAttributeName(i).equals(getAttributeName(j)))
+                            exception("Duplicate Attribute: " + getAttributeName(i));
+                    }
+                }
+                }
+        */
+        if (processNsp)
+            adjustNsp();
+        else
+            namespace = "";
+
+        elementStack[sp] = namespace;
+        elementStack[sp + 1] = prefix;
+        elementStack[sp + 2] = name;
+    }
+
+    /** 
+     * result: isWhitespace; if the setName parameter is set,
+     * the name of the entity is stored in "name" */
+
+    private final void pushEntity()
+        throws IOException, XmlPullParserException {
+
+        push(read()); // &
+        
+        
+        int pos = txtPos;
+
+        while (true) {
+            int c = read();
+            if (c == ';')
+                break;
+            if (c < 128
+                && (c < '0' || c > '9')
+                && (c < 'a' || c > 'z')
+                && (c < 'A' || c > 'Z')
+                && c != '_'
+                && c != '-'
+                && c != '#') {
+                if(!relaxed){
+                    error("unterminated entity ref");
+                }
+                //; ends with:"+(char)c);           
+                if (c != -1)
+                    push(c);
+                return;
+            }
+
+            push(c);
+        }
+
+        String code = get(pos);
+        txtPos = pos - 1;
+        if (token && type == ENTITY_REF){
+            name = code;
+        }
+
+        if (code.charAt(0) == '#') {
+            int c =
+                (code.charAt(1) == 'x'
+                    ? Integer.parseInt(code.substring(2), 16)
+                    : Integer.parseInt(code.substring(1)));
+            push(c);
+            return;
+        }
+
+        String result = (String) entityMap.get(code);
+
+        unresolved = result == null;
+
+        if (unresolved) {
+            if (!token)
+                error("unresolved: &" + code + ";");
+        }
+        else {
+            for (int i = 0; i < result.length(); i++)
+                push(result.charAt(i));
+        }
+    }
+
+    /** types:
+    '<': parse to any token (for nextToken ())
+    '"': parse to quote
+    ' ': parse to whitespace or '>'
+    */
+
+    private final void pushText(int delimiter, boolean resolveEntities)
+        throws IOException, XmlPullParserException {
+
+        int next = peek(0);
+        int cbrCount = 0;
+
+        while (next != -1 && next != delimiter) { // covers eof, '<', '"'
+
+            if (delimiter == ' ')
+                if (next <= ' ' || next == '>')
+                    break;
+
+            if (next == '&') {
+                if (!resolveEntities)
+                    break;
+
+                pushEntity();
+            }
+            else if (next == '\n' && type == START_TAG) {
+                read();
+                push(' ');
+            }
+            else
+                push(read());
+
+            if (next == '>' && cbrCount >= 2 && delimiter != ']')
+                error("Illegal: ]]>");
+
+            if (next == ']')
+                cbrCount++;
+            else
+                cbrCount = 0;
+
+            next = peek(0);
+        }
+    }
+
+    private final void read(char c)
+        throws IOException, XmlPullParserException {
+        int a = read();
+        if (a != c)
+            error("expected: '" + c + "' actual: '" + ((char) a) + "'");
+    }
+
+    private final int read() throws IOException {
+        int result;
+
+        if (peekCount == 0)
+            result = peek(0);
+        else {
+            result = peek[0];
+            peek[0] = peek[1];
+        }
+        //        else {
+        //            result = peek[0]; 
+        //            System.arraycopy (peek, 1, peek, 0, peekCount-1);
+        //        }
+        peekCount--;
+
+        column++;
+
+        if (result == '\n') {
+
+            line++;
+            column = 1;
+        }
+
+        return result;
+    }
+
+    /** Does never read more than needed */
+
+    private final int peek(int pos) throws IOException {
+
+        while (pos >= peekCount) {
+
+            int nw;
+
+            if (srcBuf.length <= 1)
+                nw = reader.read();
+            else if (srcPos < srcCount)
+                nw = srcBuf[srcPos++];
+            else {
+                srcCount = reader.read(srcBuf, 0, srcBuf.length);
+                if (srcCount <= 0)
+                    nw = -1;
+                else
+                    nw = srcBuf[0];
+
+                srcPos = 1;
+            }
+
+            if (nw == '\r') {
+                wasCR = true;
+                peek[peekCount++] = '\n';
+            }
+            else {
+                if (nw == '\n') {
+                    if (!wasCR)
+                        peek[peekCount++] = '\n';
+                }
+                else
+                    peek[peekCount++] = nw;
+
+                wasCR = false;
+            }
+        }
+
+        return peek[pos];
+    }
+
+    private final String readName()
+        throws IOException, XmlPullParserException {
+
+        int pos = txtPos;
+        int c = peek(0);
+        if ((c < 'a' || c > 'z')
+            && (c < 'A' || c > 'Z')
+            && c != '_'
+            && c != ':'
+            && c < 0x0c0
+            && !relaxed)
+            error("name expected");
+
+        do {
+            push(read());
+            c = peek(0);
+        }
+        while ((c >= 'a' && c <= 'z')
+            || (c >= 'A' && c <= 'Z')
+            || (c >= '0' && c <= '9')
+            || c == '_'
+            || c == '-'
+            || c == ':'
+            || c == '.'
+            || c >= 0x0b7);
+
+        String result = get(pos);
+        txtPos = pos;
+        return result;
+    }
+
+    private final void skip() throws IOException {
+
+        while (true) {
+            int c = peek(0);
+            if (c > ' ' || c == -1)
+                break;
+            read();
+        }
+    }
+
+    //  public part starts here...
+
+    public void setInput(Reader reader) throws XmlPullParserException {
+        this.reader = reader;
+
+        line = 1;
+        column = 0;
+        type = START_DOCUMENT;
+        name = null;
+        namespace = null;
+        degenerated = false;
+        attributeCount = -1;
+        encoding = null;
+        version = null;
+        standalone = null;
+
+        if (reader == null)
+            return;
+
+        srcPos = 0;
+        srcCount = 0;
+        peekCount = 0;
+        depth = 0;
+
+        entityMap = new Hashtable();
+        entityMap.put("amp", "&");
+        entityMap.put("apos", "'");
+        entityMap.put("gt", ">");
+        entityMap.put("lt", "<");
+        entityMap.put("quot", "\"");
+    }
+
+    public void setInput(InputStream is, String _enc)
+        throws XmlPullParserException {
+
+        srcPos = 0;
+        srcCount = 0;
+        String enc = _enc;
+
+        if (is == null)
+            throw new IllegalArgumentException();
+
+        try {
+
+            if (enc == null) {
+                // read four bytes 
+
+                int chk = 0;
+
+                while (srcCount < 4) {
+                    int i = is.read();
+                    if (i == -1)
+                        break;
+                    chk = (chk << 8) | i;
+                    srcBuf[srcCount++] = (char) i;
+                }
+
+                if (srcCount == 4) {
+                    switch (chk) {
+                        case 0x00000FEFF :
+                            enc = "UTF-32BE";
+                            srcCount = 0;
+                            break;
+
+                        case 0x0FFFE0000 :
+                            enc = "UTF-32LE";
+                            srcCount = 0;
+                            break;
+
+                        case 0x03c :
+                            enc = "UTF-32BE";
+                            srcBuf[0] = '<';
+                            srcCount = 1;
+                            break;
+
+                        case 0x03c000000 :
+                            enc = "UTF-32LE";
+                            srcBuf[0] = '<';
+                            srcCount = 1;
+                            break;
+
+                        case 0x0003c003f :
+                            enc = "UTF-16BE";
+                            srcBuf[0] = '<';
+                            srcBuf[1] = '?';
+                            srcCount = 2;
+                            break;
+
+                        case 0x03c003f00 :
+                            enc = "UTF-16LE";
+                            srcBuf[0] = '<';
+                            srcBuf[1] = '?';
+                            srcCount = 2;
+                            break;
+
+                        case 0x03c3f786d :
+                            while (true) {
+                                int i = is.read();
+                                if (i == -1)
+                                    break;
+                                srcBuf[srcCount++] = (char) i;
+                                if (i == '>') {
+                                    String s = new String(srcBuf, 0, srcCount);
+                                    int i0 = s.indexOf("encoding");
+                                    if (i0 != -1) {
+                                        while (s.charAt(i0) != '"'
+                                            && s.charAt(i0) != '\'')
+                                            i0++;
+                                        char deli = s.charAt(i0++);
+                                        int i1 = s.indexOf(deli, i0);
+                                        enc = s.substring(i0, i1);
+                                    }
+                                    break;
+                                }
+                            }
+
+                        default :
+                            if ((chk & 0x0ffff0000) == 0x0FEFF0000) {
+                                enc = "UTF-16BE";
+                                srcBuf[0] =
+                                    (char) ((srcBuf[2] << 8) | srcBuf[3]);
+                                srcCount = 1;
+                            }
+                            else if ((chk & 0x0ffff0000) == 0x0fffe0000) {
+                                enc = "UTF-16LE";
+                                srcBuf[0] =
+                                    (char) ((srcBuf[3] << 8) | srcBuf[2]);
+                                srcCount = 1;
+                            }
+                            else if ((chk & 0x0ffffff00) == 0x0EFBBBF00) {
+                                enc = "UTF-8";
+                                srcBuf[0] = srcBuf[3];
+                                srcCount = 1;
+                            }
+                    }
+                }
+            }
+
+            if (enc == null)
+                enc = "UTF-8";
+
+            int sc = srcCount;
+            setInput(new InputStreamReader(is, enc));
+            encoding = _enc;
+            srcCount = sc;
+        }
+        catch (Exception e) {
+            throw new XmlPullParserException(
+                "Invalid stream or encoding: " + e.toString(),
+                this,
+                e);
+        }
+    }
+
+    public boolean getFeature(String feature) {
+        if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature))
+            return processNsp;
+        else if (isProp(feature, false, "relaxed"))
+            return relaxed;
+        else
+            return false;
+    }
+
+    public String getInputEncoding() {
+        return encoding;
+    }
+
+    public void defineEntityReplacementText(String entity, String value)
+        throws XmlPullParserException {
+        if (entityMap == null)
+            throw new RuntimeException("entity replacement text must be defined after setInput!");
+        entityMap.put(entity, value);
+    }
+
+    public Object getProperty(String property) {
+        if (isProp(property, true, "xmldecl-version"))
+            return version;
+        if (isProp(property, true, "xmldecl-standalone"))
+            return standalone;
+        if (isProp(property, true, "location"))            
+            return location != null ? location : reader.toString();
+        return null;
+    }
+
+    public int getNamespaceCount(int depth) {
+        if (depth > this.depth)
+            throw new IndexOutOfBoundsException();
+        return nspCounts[depth];
+    }
+
+    public String getNamespacePrefix(int pos) {
+        return nspStack[pos << 1];
+    }
+
+    public String getNamespaceUri(int pos) {
+        return nspStack[(pos << 1) + 1];
+    }
+
+    public String getNamespace(String prefix) {
+
+        if ("xml".equals(prefix))
+            return "http://www.w3.org/XML/1998/namespace";
+        if ("xmlns".equals(prefix))
+            return "http://www.w3.org/2000/xmlns/";
+
+        for (int i = (getNamespaceCount(depth) << 1) - 2; i >= 0; i -= 2) {
+            if (prefix == null) {
+                if (nspStack[i] == null)
+                    return nspStack[i + 1];
+            }
+            else if (prefix.equals(nspStack[i]))
+                return nspStack[i + 1];
+        }
+        return null;
+    }
+
+    public int getDepth() {
+        return depth;
+    }
+
+    public String getPositionDescription() {
+
+        StringBuffer buf =
+            new StringBuffer(type < TYPES.length ? TYPES[type] : "unknown");
+        buf.append(' ');
+
+        if (type == START_TAG || type == END_TAG) {
+            if (degenerated)
+                buf.append("(empty) ");
+            buf.append('<');
+            if (type == END_TAG)
+                buf.append('/');
+
+            if (prefix != null)
+                buf.append("{" + namespace + "}" + prefix + ":");
+            buf.append(name);
+
+            int cnt = attributeCount << 2;
+            for (int i = 0; i < cnt; i += 4) {
+                buf.append(' ');
+                if (attributes[i + 1] != null)
+                    buf.append(
+                        "{" + attributes[i] + "}" + attributes[i + 1] + ":");
+                buf.append(attributes[i + 2] + "='" + attributes[i + 3] + "'");
+            }
+
+            buf.append('>');
+        }
+        else if (type == IGNORABLE_WHITESPACE);
+        else if (type != TEXT)
+            buf.append(getText());
+        else if (isWhitespace)
+            buf.append("(whitespace)");
+        else {
+            String text = getText();
+            if (text.length() > 16)
+                text = text.substring(0, 16) + "...";
+            buf.append(text);
+        }
+
+        buf.append("@"+line + ":" + column);
+        if(location != null){
+            buf.append(" in ");
+            buf.append(location);
+        }
+        else if(reader != null){
+            buf.append(" in ");
+            buf.append(reader.toString());
+        }
+        return buf.toString();
+    }
+
+    public int getLineNumber() {
+        return line;
+    }
+
+    public int getColumnNumber() {
+        return column;
+    }
+
+    public boolean isWhitespace() throws XmlPullParserException {
+        if (type != TEXT && type != IGNORABLE_WHITESPACE && type != CDSECT)
+            exception(ILLEGAL_TYPE);
+        return isWhitespace;
+    }
+
+    public String getText() {
+        return type < TEXT
+            || (type == ENTITY_REF && unresolved) ? null : get(0);
+    }
+
+    public char[] getTextCharacters(int[] poslen) {
+        if (type >= TEXT) {
+            if (type == ENTITY_REF) {
+                poslen[0] = 0;
+                poslen[1] = name.length();
+                return name.toCharArray();
+            }
+            poslen[0] = 0;
+            poslen[1] = txtPos;
+            return txtBuf;
+        }
+
+        poslen[0] = -1;
+        poslen[1] = -1;
+        return null;
+    }
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public String getPrefix() {
+        return prefix;
+    }
+
+    public boolean isEmptyElementTag() throws XmlPullParserException {
+        if (type != START_TAG)
+            exception(ILLEGAL_TYPE);
+        return degenerated;
+    }
+
+    public int getAttributeCount() {
+        return attributeCount;
+    }
+
+    public String getAttributeType(int index) {
+        return "CDATA";
+    }
+
+    public boolean isAttributeDefault(int index) {
+        return false;
+    }
+
+    public String getAttributeNamespace(int index) {
+        if (index >= attributeCount)
+            throw new IndexOutOfBoundsException();
+        return attributes[index << 2];
+    }
+
+    public String getAttributeName(int index) {
+        if (index >= attributeCount)
+            throw new IndexOutOfBoundsException();
+        return attributes[(index << 2) + 2];
+    }
+
+    public String getAttributePrefix(int index) {
+        if (index >= attributeCount)
+            throw new IndexOutOfBoundsException();
+        return attributes[(index << 2) + 1];
+    }
+
+    public String getAttributeValue(int index) {
+        if (index >= attributeCount)
+            throw new IndexOutOfBoundsException();
+        return attributes[(index << 2) + 3];
+    }
+
+    public String getAttributeValue(String namespace, String name) {
+
+        for (int i = (attributeCount << 2) - 4; i >= 0; i -= 4) {
+            if (attributes[i + 2].equals(name)
+                && (namespace == null || attributes[i].equals(namespace)))
+                return attributes[i + 3];
+        }
+
+        return null;
+    }
+
+    public int getEventType() throws XmlPullParserException {
+        return type;
+    }
+
+    public int next() throws XmlPullParserException, IOException {
+
+        txtPos = 0;
+        isWhitespace = true;
+        int minType = 9999;
+        token = false;
+
+        do {
+            nextImpl();
+            if (type < minType)
+                minType = type;
+            //        if (curr <= TEXT) type = curr; 
+        }
+        while (minType > ENTITY_REF // ignorable
+            || (minType >= TEXT && peekType() >= TEXT));
+
+        type = minType;
+        if (type > TEXT)
+            type = TEXT;
+
+        return type;
+    }
+
+    public int nextToken() throws XmlPullParserException, IOException {
+
+        isWhitespace = true;
+        txtPos = 0;
+
+        token = true;
+        nextImpl();
+        return type;
+    }
+
+    //
+    // utility methods to make XML parsing easier ...
+
+    public int nextTag() throws XmlPullParserException, IOException {
+
+        next();
+        if (type == TEXT && isWhitespace)
+            next();
+
+        if (type != END_TAG && type != START_TAG)
+            exception("unexpected type");
+
+        return type;
+    }
+
+    public void require(int type, String namespace, String name)
+        throws XmlPullParserException, IOException {
+
+        if (type != this.type
+            || (namespace != null && !namespace.equals(getNamespace()))
+            || (name != null && !name.equals(getName())))
+            exception(
+                "expected: " + TYPES[type] + " {" + namespace + "}" + name);
+    }
+
+    public String nextText() throws XmlPullParserException, IOException {
+        if (type != START_TAG)
+            exception("precondition: START_TAG");
+
+        next();
+
+        String result;
+
+        if (type == TEXT) {
+            result = getText();
+            next();
+        }
+        else
+            result = "";
+
+        if (type != END_TAG)
+            exception("END_TAG expected");
+
+        return result;
+    }
+
+    public void setFeature(String feature, boolean value)
+        throws XmlPullParserException {
+        if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature))
+            processNsp = value;
+        else if (isProp(feature, false, "relaxed"))
+            relaxed = value;
+        else
+            exception("unsupported feature: " + feature);
+    }
+
+    public void setProperty(String property, Object value)
+        throws XmlPullParserException {
+        if(isProp(property, true, "location"))
+            location = value;
+        else
+            throw new XmlPullParserException("unsupported property: " + property);
+    }
+
+    /**
+      * Skip sub tree that is currently porser positioned on.
+      * <br>NOTE: parser must be on START_TAG and when funtion returns
+      * parser will be positioned on corresponding END_TAG. 
+      */
+
+    //    Implementation copied from Alek's mail... 
+
+    public void skipSubTree() throws XmlPullParserException, IOException {
+        require(START_TAG, null, null);
+        int level = 1;
+        while (level > 0) {
+            int eventType = next();
+            if (eventType == END_TAG) {
+                --level;
+            }
+            else if (eventType == START_TAG) {
+                ++level;
+            }
+        }
+    }
+}
diff --git a/xml/src/main/java/org/kxml2/io/KXmlSerializer.java b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
new file mode 100644
index 0000000..d63ed04
--- /dev/null
+++ b/xml/src/main/java/org/kxml2/io/KXmlSerializer.java
@@ -0,0 +1,562 @@
+/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The  above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE. */
+ 
+
+package org.kxml2.io;
+
+import java.io.*;
+import org.xmlpull.v1.*;
+
+public class KXmlSerializer implements XmlSerializer {
+
+    //    static final String UNDEFINED = ":";
+
+    // BEGIN android-added
+    /** size (in characters) for the write buffer */
+    private static final int WRITE_BUFFER_SIZE = 500;
+    // END android-added   
+
+    // BEGIN android-changed
+    // (Guarantee that the writer is always buffered.)
+    private BufferedWriter writer;
+    // END android-changed
+
+    private boolean pending;
+    private int auto;
+    private int depth;
+
+    private String[] elementStack = new String[12];
+    //nsp/prefix/name
+    private int[] nspCounts = new int[4];
+    private String[] nspStack = new String[8];
+    //prefix/nsp; both empty are ""
+    private boolean[] indent = new boolean[4];
+    private boolean unicode;
+    private String encoding;
+
+    private final void check(boolean close) throws IOException {
+        if (!pending)
+            return;
+
+        depth++;
+        pending = false;
+
+        if (indent.length <= depth) {
+            boolean[] hlp = new boolean[depth + 4];
+            System.arraycopy(indent, 0, hlp, 0, depth);
+            indent = hlp;
+        }
+        indent[depth] = indent[depth - 1];
+
+        for (int i = nspCounts[depth - 1];
+            i < nspCounts[depth];
+            i++) {
+            writer.write(' ');
+            writer.write("xmlns");
+            if (!"".equals(nspStack[i * 2])) {
+                writer.write(':');
+                writer.write(nspStack[i * 2]);
+            }
+            else if ("".equals(getNamespace()) && !"".equals(nspStack[i * 2 + 1]))
+                throw new IllegalStateException("Cannot set default namespace for elements in no namespace");
+            writer.write("=\"");
+            writeEscaped(nspStack[i * 2 + 1], '"');
+            writer.write('"');
+        }
+
+        if (nspCounts.length <= depth + 1) {
+            int[] hlp = new int[depth + 8];
+            System.arraycopy(nspCounts, 0, hlp, 0, depth + 1);
+            nspCounts = hlp;
+        }
+
+        nspCounts[depth + 1] = nspCounts[depth];
+        //   nspCounts[depth + 2] = nspCounts[depth];
+
+        writer.write(close ? " />" : ">");
+    }
+
+    private final void writeEscaped(String s, int quot)
+        throws IOException {
+
+        for (int i = 0; i < s.length(); i++) {
+            char c = s.charAt(i);
+            switch (c) {
+                case '\n':
+                case '\r':
+                case '\t':
+                    if(quot == -1) 
+                        writer.write(c);
+                    else 
+                        writer.write("&#"+((int) c)+';');
+                    break;
+                case '&' :
+                    writer.write("&amp;");
+                    break;
+                case '>' :
+                    writer.write("&gt;");
+                    break;
+                case '<' :
+                    writer.write("&lt;");
+                    break;
+                case '"' :
+                case '\'' :
+                    if (c == quot) {
+                        writer.write(
+                            c == '"' ? "&quot;" : "&apos;");
+                        break;
+                    }
+                default :
+                    //if(c < ' ')
+                    //    throw new IllegalArgumentException("Illegal control code:"+((int) c));
+
+                    if (c >= ' ' && c !='@' && (c < 127 || unicode))
+                        writer.write(c);
+                    else
+                        writer.write("&#" + ((int) c) + ";");
+
+            }
+        }
+    }
+
+    /*
+        private final void writeIndent() throws IOException {
+            writer.write("\r\n");
+            for (int i = 0; i < depth; i++)
+                writer.write(' ');
+        }*/
+
+    public void docdecl(String dd) throws IOException {
+        writer.write("<!DOCTYPE");
+        writer.write(dd);
+        writer.write(">");
+    }
+
+    public void endDocument() throws IOException {
+        while (depth > 0) {
+            endTag(
+                elementStack[depth * 3 - 3],
+                elementStack[depth * 3 - 1]);
+        }
+        flush();
+    }
+
+    public void entityRef(String name) throws IOException {
+        check(false);
+        writer.write('&');
+        writer.write(name);
+        writer.write(';');
+    }
+
+    public boolean getFeature(String name) {
+        //return false;
+        return (
+            "http://xmlpull.org/v1/doc/features.html#indent-output"
+                .equals(
+                name))
+            ? indent[depth]
+            : false;
+    }
+
+    public String getPrefix(String namespace, boolean create) {
+        try {
+            return getPrefix(namespace, false, create);
+        }
+        catch (IOException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+
+    private final String getPrefix(
+        String namespace,
+        boolean includeDefault,
+        boolean create)
+        throws IOException {
+
+        for (int i = nspCounts[depth + 1] * 2 - 2;
+            i >= 0;
+            i -= 2) {
+            if (nspStack[i + 1].equals(namespace)
+                && (includeDefault || !nspStack[i].equals(""))) {
+                String cand = nspStack[i];
+                for (int j = i + 2;
+                    j < nspCounts[depth + 1] * 2;
+                    j++) {
+                    if (nspStack[j].equals(cand)) {
+                        cand = null;
+                        break;
+                    }
+                }
+                if (cand != null)
+                    return cand;
+            }
+        }
+
+        if (!create)
+            return null;
+
+        String prefix;
+
+        if ("".equals(namespace))
+            prefix = "";
+        else {
+            do {
+                prefix = "n" + (auto++);
+                for (int i = nspCounts[depth + 1] * 2 - 2;
+                    i >= 0;
+                    i -= 2) {
+                    if (prefix.equals(nspStack[i])) {
+                        prefix = null;
+                        break;
+                    }
+                }
+            }
+            while (prefix == null);
+        }
+
+        boolean p = pending;
+        pending = false;
+        setPrefix(prefix, namespace);
+        pending = p;
+        return prefix;
+    }
+
+    public Object getProperty(String name) {
+        throw new RuntimeException("Unsupported property");
+    }
+
+    public void ignorableWhitespace(String s)
+        throws IOException {
+        text(s);
+    }
+
+    public void setFeature(String name, boolean value) {
+        if ("http://xmlpull.org/v1/doc/features.html#indent-output"
+            .equals(name)) {
+            indent[depth] = value;
+        }
+        else
+            throw new RuntimeException("Unsupported Feature");
+    }
+
+    public void setProperty(String name, Object value) {
+        throw new RuntimeException(
+            "Unsupported Property:" + value);
+    }
+
+    public void setPrefix(String prefix, String namespace)
+        throws IOException {
+
+        check(false);
+        if (prefix == null)
+            prefix = "";
+        if (namespace == null)
+            namespace = "";
+
+        String defined = getPrefix(namespace, true, false);
+
+        // boil out if already defined
+
+        if (prefix.equals(defined))
+            return;
+
+        int pos = (nspCounts[depth + 1]++) << 1;
+
+        if (nspStack.length < pos + 1) {
+            String[] hlp = new String[nspStack.length + 16];
+            System.arraycopy(nspStack, 0, hlp, 0, pos);
+            nspStack = hlp;
+        }
+
+        nspStack[pos++] = prefix;
+        nspStack[pos] = namespace;
+    }
+
+    public void setOutput(Writer writer) {
+        // BEGIN android-changed
+        // Guarantee that the writer is always buffered.
+        if (writer instanceof BufferedWriter) {
+            this.writer = (BufferedWriter) writer;
+        } else {
+            this.writer = new BufferedWriter(writer, WRITE_BUFFER_SIZE);
+        }
+        // END android-changed
+
+        // elementStack = new String[12]; //nsp/prefix/name
+        //nspCounts = new int[4];
+        //nspStack = new String[8]; //prefix/nsp
+        //indent = new boolean[4];
+
+        nspCounts[0] = 2;
+        nspCounts[1] = 2;
+        nspStack[0] = "";
+        nspStack[1] = "";
+        nspStack[2] = "xml";
+        nspStack[3] = "http://www.w3.org/XML/1998/namespace";
+        pending = false;
+        auto = 0;
+        depth = 0;
+
+        unicode = false;
+    }
+
+    public void setOutput(OutputStream os, String encoding)
+        throws IOException {
+        if (os == null)
+            throw new IllegalArgumentException();
+        setOutput(
+            encoding == null
+                ? new OutputStreamWriter(os)
+                : new OutputStreamWriter(os, encoding));
+        this.encoding = encoding;
+        if (encoding != null
+            && encoding.toLowerCase().startsWith("utf"))
+            unicode = true;
+    }
+
+    public void startDocument(
+        String encoding,
+        Boolean standalone)
+        throws IOException {
+        writer.write("<?xml version='1.0' ");
+
+        if (encoding != null) {
+            this.encoding = encoding;
+            if (encoding.toLowerCase().startsWith("utf"))
+                unicode = true;
+        }
+
+        if (this.encoding != null) {
+            writer.write("encoding='");
+            writer.write(this.encoding);
+            writer.write("' ");
+        }
+
+        if (standalone != null) {
+            writer.write("standalone='");
+            writer.write(
+                standalone.booleanValue() ? "yes" : "no");
+            writer.write("' ");
+        }
+        writer.write("?>");
+    }
+
+    public XmlSerializer startTag(String namespace, String name)
+        throws IOException {
+        check(false);
+
+        //        if (namespace == null)
+        //            namespace = "";
+
+        if (indent[depth]) {
+            writer.write("\r\n");
+            for (int i = 0; i < depth; i++)
+                writer.write("  ");
+        }
+
+        int esp = depth * 3;
+
+        if (elementStack.length < esp + 3) {
+            String[] hlp = new String[elementStack.length + 12];
+            System.arraycopy(elementStack, 0, hlp, 0, esp);
+            elementStack = hlp;
+        }
+
+        String prefix =
+            namespace == null
+                ? ""
+                : getPrefix(namespace, true, true);
+
+        if ("".equals(namespace)) {
+            for (int i = nspCounts[depth];
+                i < nspCounts[depth + 1];
+                i++) {
+                if ("".equals(nspStack[i * 2]) && !"".equals(nspStack[i * 2 + 1])) {
+                    throw new IllegalStateException("Cannot set default namespace for elements in no namespace");
+                }
+            }
+        }
+
+        elementStack[esp++] = namespace;
+        elementStack[esp++] = prefix;
+        elementStack[esp] = name;
+
+        writer.write('<');
+        if (!"".equals(prefix)) {
+            writer.write(prefix);
+            writer.write(':');
+        }
+
+        writer.write(name);
+
+        pending = true;
+
+        return this;
+    }
+
+    public XmlSerializer attribute(
+        String namespace,
+        String name,
+        String value)
+        throws IOException {
+        if (!pending)
+            throw new IllegalStateException("illegal position for attribute");
+
+        //        int cnt = nspCounts[depth];
+
+        if (namespace == null)
+            namespace = "";
+
+        //        depth--;
+        //        pending = false;
+
+        String prefix =
+            "".equals(namespace)
+                ? ""
+                : getPrefix(namespace, false, true);
+
+        //        pending = true;
+        //        depth++;
+
+        /*        if (cnt != nspCounts[depth]) {
+                    writer.write(' ');
+                    writer.write("xmlns");
+                    if (nspStack[cnt * 2] != null) {
+                        writer.write(':');
+                        writer.write(nspStack[cnt * 2]);
+                    }
+                    writer.write("=\"");
+                    writeEscaped(nspStack[cnt * 2 + 1], '"');
+                    writer.write('"');
+                }
+                */
+
+        writer.write(' ');
+        if (!"".equals(prefix)) {
+            writer.write(prefix);
+            writer.write(':');
+        }
+        writer.write(name);
+        writer.write('=');
+        char q = value.indexOf('"') == -1 ? '"' : '\'';
+        writer.write(q);
+        writeEscaped(value, q);
+        writer.write(q);
+
+        return this;
+    }
+
+    public void flush() throws IOException {
+        check(false);
+        writer.flush();
+    }
+    /*
+        public void close() throws IOException {
+            check();
+            writer.close();
+        }
+    */
+    public XmlSerializer endTag(String namespace, String name)
+        throws IOException {
+
+        if (!pending)
+            depth--;
+        //        if (namespace == null)
+        //          namespace = "";
+
+        if ((namespace == null
+            && elementStack[depth * 3] != null)
+            || (namespace != null
+                && !namespace.equals(elementStack[depth * 3]))
+            || !elementStack[depth * 3 + 2].equals(name))
+            throw new IllegalArgumentException("</{"+namespace+"}"+name+"> does not match start");
+
+        if (pending) {
+            check(true);
+            depth--;
+        }
+        else {
+            if (indent[depth + 1]) {
+                writer.write("\r\n");
+                for (int i = 0; i < depth; i++)
+                    writer.write("  ");
+            }
+
+            writer.write("</");
+            String prefix = elementStack[depth * 3 + 1];
+            if (!"".equals(prefix)) {
+                writer.write(prefix);
+                writer.write(':');
+            }
+            writer.write(name);
+            writer.write('>');
+        }
+
+        nspCounts[depth + 1] = nspCounts[depth];
+        return this;
+    }
+
+    public String getNamespace() {
+        return getDepth() == 0 ? null : elementStack[getDepth() * 3 - 3];
+    }
+
+    public String getName() {
+        return getDepth() == 0 ? null : elementStack[getDepth() * 3 - 1];
+    }
+
+    public int getDepth() {
+        return pending ? depth + 1 : depth;
+    }
+
+    public XmlSerializer text(String text) throws IOException {
+        check(false);
+        indent[depth] = false;
+        writeEscaped(text, -1);
+        return this;
+    }
+
+    public XmlSerializer text(char[] text, int start, int len)
+        throws IOException {
+        text(new String(text, start, len));
+        return this;
+    }
+
+    public void cdsect(String data) throws IOException {
+        check(false);
+        writer.write("<![CDATA[");
+        writer.write(data);
+        writer.write("]]>");
+    }
+
+    public void comment(String comment) throws IOException {
+        check(false);
+        writer.write("<!--");
+        writer.write(comment);
+        writer.write("-->");
+    }
+
+    public void processingInstruction(String pi)
+        throws IOException {
+        check(false);
+        writer.write("<?");
+        writer.write(pi);
+        writer.write("?>");
+    }
+}
diff --git a/xml/src/main/java/org/kxml2/kdom/Document.java b/xml/src/main/java/org/kxml2/kdom/Document.java
new file mode 100644
index 0000000..859334c
--- /dev/null
+++ b/xml/src/main/java/org/kxml2/kdom/Document.java
@@ -0,0 +1,129 @@
+/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The  above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE. */
+ 
+
+package org.kxml2.kdom;
+
+import java.io.*;
+
+import org.xmlpull.v1.*;
+/** The document consists of some legacy events and a single root
+    element. This class basically adds some consistency checks to
+    Node. */
+
+public class Document extends Node {
+
+    protected int rootIndex = -1;
+    String encoding;
+    Boolean standalone;
+
+    /** returns "#document" */
+
+    public String getEncoding () {
+        return encoding;
+    }
+    
+    public void setEncoding(String enc) {
+        this.encoding = enc;
+    }
+    
+    public void setStandalone (Boolean standalone) {
+        this.standalone = standalone;
+    }
+    
+    public Boolean getStandalone() {
+        return standalone;
+    }
+
+
+    public String getName() {
+        return "#document";
+    }
+
+    /** Adds a child at the given index position. Throws
+    an exception when a second root element is added */
+
+    public void addChild(int index, int type, Object child) {
+        if (type == ELEMENT) {
+         //   if (rootIndex != -1)
+           //     throw new RuntimeException("Only one document root element allowed");
+
+            rootIndex = index;
+        }
+        else if (rootIndex >= index)
+            rootIndex++;
+
+        super.addChild(index, type, child);
+    }
+
+    /** reads the document and checks if the last event
+    is END_DOCUMENT. If not, an exception is thrown.
+    The end event is consumed. For parsing partial
+        XML structures, consider using Node.parse (). */
+
+    public void parse(XmlPullParser parser)
+        throws IOException, XmlPullParserException {
+
+        parser.require(XmlPullParser.START_DOCUMENT, null, null);
+        parser.nextToken ();            
+
+        encoding = parser.getInputEncoding();
+        standalone = (Boolean)parser.getProperty ("http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone");
+        
+        super.parse(parser);
+
+        if (parser.getEventType() != XmlPullParser.END_DOCUMENT)
+            throw new RuntimeException("Document end expected!");
+
+    }
+
+    public void removeChild(int index) {
+        if (index == rootIndex)
+            rootIndex = -1;
+        else if (index < rootIndex)
+            rootIndex--;
+
+        super.removeChild(index);
+    }
+
+    /** returns the root element of this document. */
+
+    public Element getRootElement() {
+        if (rootIndex == -1)
+            throw new RuntimeException("Document has no root element!");
+
+        return (Element) getChild(rootIndex);
+    }
+    
+    
+    /** Writes this node to the given XmlWriter. For node and document,
+        this method is identical to writeChildren, except that the
+        stream is flushed automatically. */
+
+    public void write(XmlSerializer writer)
+        throws IOException {
+        
+        writer.startDocument(encoding, standalone);
+        writeChildren(writer);
+        writer.endDocument();
+    }
+    
+    
+}
\ No newline at end of file
diff --git a/xml/src/main/java/org/kxml2/kdom/Element.java b/xml/src/main/java/org/kxml2/kdom/Element.java
new file mode 100644
index 0000000..6a5777b
--- /dev/null
+++ b/xml/src/main/java/org/kxml2/kdom/Element.java
@@ -0,0 +1,335 @@
+/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The  above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE. */
+
+package org.kxml2.kdom;
+
+import java.io.*;
+import java.util.*;
+
+import org.xmlpull.v1.*;
+
+/** 
+ * In order to create an element, please use the createElement method
+ * instead of invoking the constructor directly. The right place to
+ * add user defined initialization code is the init method. */
+
+public class Element extends Node {
+
+    protected String namespace;
+    protected String name;
+    protected Vector attributes;
+    protected Node parent;
+    protected Vector prefixes;
+
+    public Element() {
+    }
+
+    /** 
+     * called when all properties are set, but before children
+     * are parsed. Please do not use setParent for initialization
+     * code any longer. */
+
+    public void init() {
+    }
+
+
+
+
+    /** 
+     * removes all children and attributes */
+
+    public void clear() {
+        attributes = null;
+        children = null;
+    }
+
+    /** 
+     * Forwards creation request to parent if any, otherwise
+     * calls super.createElement. */
+
+    public Element createElement(
+        String namespace,
+        String name) { 
+
+        return (this.parent == null)
+            ? super.createElement(namespace, name)
+            : this.parent.createElement(namespace, name);
+    }
+
+    /** 
+     * Returns the number of attributes of this element. */
+
+    public int getAttributeCount() {
+        return attributes == null ? 0 : attributes.size();
+    }
+
+    public String getAttributeNamespace (int index) {
+        return ((String []) attributes.elementAt (index)) [0];
+    }
+
+/*    public String getAttributePrefix (int index) {
+        return ((String []) attributes.elementAt (index)) [1];
+    }*/
+
+    public String getAttributeName (int index) {
+        return ((String []) attributes.elementAt (index)) [1];
+    }
+    
+
+    public String getAttributeValue (int index) {
+        return ((String []) attributes.elementAt (index)) [2];
+    }
+    
+    
+    public String getAttributeValue (String namespace, String name) {
+        for (int i = 0; i < getAttributeCount (); i++) {
+            if (name.equals (getAttributeName (i)) 
+                && (namespace == null || namespace.equals (getAttributeNamespace(i)))) {
+                return getAttributeValue (i);
+            }
+        }                        
+        return null;            
+    }
+
+    /** 
+     * Returns the root node, determined by ascending to the 
+     * all parents un of the root element. */
+
+    public Node getRoot() {
+
+        Element current = this;
+        
+        while (current.parent != null) {
+            if (!(current.parent instanceof Element)) return current.parent;
+            current = (Element) current.parent;
+        }
+        
+        return current;
+    }
+
+    /** 
+     * returns the (local) name of the element */
+
+    public String getName() {
+        return name;
+    }
+
+    /** 
+     * returns the namespace of the element */
+
+    public String getNamespace() {
+        return namespace;
+    }
+
+
+    /** 
+     * returns the namespace for the given prefix */
+    
+    public String getNamespaceUri (String prefix) {
+        int cnt = getNamespaceCount ();
+        for (int i = 0; i < cnt; i++) {
+            if (prefix == getNamespacePrefix (i) ||
+                (prefix != null && prefix.equals (getNamespacePrefix (i))))
+                return getNamespaceUri (i);    
+        }
+        return parent instanceof Element ? ((Element) parent).getNamespaceUri (prefix) : null;
+    }
+
+
+    /** 
+     * returns the number of declared namespaces, NOT including
+     * parent elements */
+
+    public int getNamespaceCount () {
+        return (prefixes == null ? 0 : prefixes.size ());
+    }
+
+
+    public String getNamespacePrefix (int i) {
+        return ((String []) prefixes.elementAt (i)) [0];
+    }
+
+    public String getNamespaceUri (int i) {
+        return ((String []) prefixes.elementAt (i)) [1];
+    }
+
+
+    /** 
+     * Returns the parent node of this element */
+
+    public Node getParent() {
+        return parent;
+    }
+
+    /* 
+     * Returns the parent element if available, null otherwise 
+
+    public Element getParentElement() {
+        return (parent instanceof Element)
+            ? ((Element) parent)
+            : null;
+    }
+*/
+
+    /** 
+     * Builds the child elements from the given Parser. By overwriting 
+     * parse, an element can take complete control over parsing its 
+     * subtree. */
+
+    public void parse(XmlPullParser parser)
+        throws IOException, XmlPullParserException {
+
+        for (int i = parser.getNamespaceCount (parser.getDepth () - 1);
+            i < parser.getNamespaceCount (parser.getDepth ()); i++) {
+            setPrefix (parser.getNamespacePrefix (i), parser.getNamespaceUri(i));
+        }
+        
+        
+        for (int i = 0; i < parser.getAttributeCount (); i++) 
+            setAttribute (parser.getAttributeNamespace (i),
+//                          parser.getAttributePrefix (i),
+                          parser.getAttributeName (i),
+                          parser.getAttributeValue (i));
+
+
+        //        if (prefixMap == null) throw new RuntimeException ("!!");
+
+        init();
+
+
+        if (parser.isEmptyElementTag()) 
+            parser.nextToken ();
+        else {
+            parser.nextToken ();
+            super.parse(parser);
+
+            if (getChildCount() == 0)
+                addChild(IGNORABLE_WHITESPACE, "");
+        }
+        
+        parser.require(
+            XmlPullParser.END_TAG,
+            getNamespace(),
+            getName());
+            
+        parser.nextToken ();
+    }
+
+
+    /** 
+     * Sets the given attribute; a value of null removes the attribute */
+
+    public void setAttribute (String namespace, String name, String value) {
+        if (attributes == null) 
+            attributes = new Vector ();
+
+        if (namespace == null) 
+            namespace = "";
+        
+        for (int i = attributes.size()-1; i >=0; i--){
+            String[] attribut = (String[]) attributes.elementAt(i);
+            if (attribut[0].equals(namespace) &&
+                attribut[1].equals(name)){
+                    
+                if (value == null) {
+                    attributes.removeElementAt(i);
+                }
+                else {
+                    attribut[2] = value;
+                }
+                return; 
+            }
+        }
+
+        attributes.addElement 
+            (new String [] {namespace, name, value});
+    }
+
+
+    /** 
+     * Sets the given prefix; a namespace value of null removess the 
+     * prefix */
+
+    public void setPrefix (String prefix, String namespace) {
+        if (prefixes == null) prefixes = new Vector ();
+        prefixes.addElement (new String [] {prefix, namespace});        
+    }
+
+
+    /** 
+     * sets the name of the element */
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    /** 
+     * sets the namespace of the element. Please note: For no
+     * namespace, please use Xml.NO_NAMESPACE, null is not a legal
+     * value. Currently, null is converted to Xml.NO_NAMESPACE, but
+     * future versions may throw an exception. */
+
+    public void setNamespace(String namespace) {
+        if (namespace == null) 
+            throw new NullPointerException ("Use \"\" for empty namespace");
+        this.namespace = namespace;
+    }
+
+    /** 
+     * Sets the Parent of this element. Automatically called from the
+     * add method.  Please use with care, you can simply
+     * create inconsitencies in the document tree structure using
+     * this method!  */
+
+    protected void setParent(Node parent) {
+        this.parent = parent;
+    }
+
+
+    /** 
+     * Writes this element and all children to the given XmlWriter. */
+
+    public void write(XmlSerializer writer)
+        throws IOException {
+
+        if (prefixes != null) {
+            for (int i = 0; i < prefixes.size(); i++) {
+                writer.setPrefix (getNamespacePrefix (i), getNamespaceUri (i));
+            }
+        }
+
+        writer.startTag(
+            getNamespace(),
+            getName());
+
+        int len = getAttributeCount();
+
+        for (int i = 0; i < len; i++) {
+            writer.attribute(
+                getAttributeNamespace(i),
+                getAttributeName(i),
+                getAttributeValue(i));
+        }
+
+        writeChildren(writer);
+
+        writer.endTag(getNamespace (), getName ());
+    }
+}
diff --git a/xml/src/main/java/org/kxml2/kdom/Node.java b/xml/src/main/java/org/kxml2/kdom/Node.java
new file mode 100644
index 0000000..a3cc78d
--- /dev/null
+++ b/xml/src/main/java/org/kxml2/kdom/Node.java
@@ -0,0 +1,366 @@
+/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The  above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE. */
+
+package org.kxml2.kdom;
+
+import java.util.*;
+import java.io.*;
+import org.xmlpull.v1.*;
+/** A common base class for Document and Element, also used for
+    storing XML fragments. */
+
+public class Node { //implements XmlIO{
+
+    public static final int DOCUMENT = 0;
+    public static final int ELEMENT = 2;
+    public static final int TEXT = 4;
+    public static final int CDSECT = 5;
+    public static final int ENTITY_REF = 6;
+    public static final int IGNORABLE_WHITESPACE = 7;
+    public static final int PROCESSING_INSTRUCTION = 8;
+    public static final int COMMENT = 9;
+    public static final int DOCDECL = 10;
+
+    protected Vector children;
+    protected StringBuffer types;
+
+    /** inserts the given child object of the given type at the
+    given index. */
+
+    public void addChild(int index, int type, Object child) {
+
+        if (child == null)
+            throw new NullPointerException();
+
+        if (children == null) {
+            children = new Vector();
+            types = new StringBuffer();
+        }
+
+        if (type == ELEMENT) {
+            if (!(child instanceof Element))
+                throw new RuntimeException("Element obj expected)");
+
+            ((Element) child).setParent(this);
+        }
+        else if (!(child instanceof String))
+            throw new RuntimeException("String expected");
+
+        children.insertElementAt(child, index);
+        types.insert(index, (char) type);
+    }
+
+    /** convenience method for addChild (getChildCount (), child) */
+
+    public void addChild(int type, Object child) {
+        addChild(getChildCount(), type, child);
+    }
+
+    /** Builds a default element with the given properties. Elements
+    should always be created using this method instead of the
+    constructor in order to enable construction of specialized
+    subclasses by deriving custom Document classes. Please note:
+    For no namespace, please use Xml.NO_NAMESPACE, null is not a
+    legal value. Currently, null is converted to Xml.NO_NAMESPACE,
+    but future versions may throw an exception. */
+
+    public Element createElement(String namespace, String name) {
+
+        Element e = new Element();
+        e.namespace = namespace == null ? "" : namespace;
+        e.name = name;
+        return e;
+    }
+
+    /** Returns the child object at the given index.  For child
+        elements, an Element object is returned. For all other child
+        types, a String is returned. */
+
+    public Object getChild(int index) {
+        return children.elementAt(index);
+    }
+
+    /** Returns the number of child objects */
+
+    public int getChildCount() {
+        return children == null ? 0 : children.size();
+    }
+
+    /** returns the element at the given index. If the node at the
+    given index is a text node, null is returned */
+
+    public Element getElement(int index) {
+        Object child = getChild(index);
+        return (child instanceof Element) ? (Element) child : null;
+    }
+
+    /** Returns the element with the given namespace and name. If the
+        element is not found, or more than one matching elements are
+        found, an exception is thrown. */
+
+    public Element getElement(String namespace, String name) {
+
+        int i = indexOf(namespace, name, 0);
+        int j = indexOf(namespace, name, i + 1);
+
+        if (i == -1 || j != -1)
+            throw new RuntimeException(
+                "Element {"
+                    + namespace
+                    + "}"
+                    + name
+                    + (i == -1 ? " not found in " : " more than once in ")
+                    + this);
+
+        return getElement(i);
+    }
+
+    /* returns "#document-fragment". For elements, the element name is returned 
+    
+    public String getName() {
+        return "#document-fragment";
+    }
+    
+    /** Returns the namespace of the current element. For Node
+        and Document, Xml.NO_NAMESPACE is returned. 
+    
+    public String getNamespace() {
+        return "";
+    }
+    
+    public int getNamespaceCount () {
+        return 0;
+    }
+    
+    /** returns the text content if the element has text-only
+    content. Throws an exception for mixed content
+    
+    public String getText() {
+    
+        StringBuffer buf = new StringBuffer();
+        int len = getChildCount();
+    
+        for (int i = 0; i < len; i++) {
+            if (isText(i))
+                buf.append(getText(i));
+            else if (getType(i) == ELEMENT)
+                throw new RuntimeException("not text-only content!");
+        }
+    
+        return buf.toString();
+    }
+    */
+
+    /** Returns the text node with the given index or null if the node
+        with the given index is not a text node. */
+
+    public String getText(int index) {
+        return (isText(index)) ? (String) getChild(index) : null;
+    }
+
+    /** Returns the type of the child at the given index. Possible 
+    types are ELEMENT, TEXT, COMMENT, and PROCESSING_INSTRUCTION */
+
+    public int getType(int index) {
+        return types.charAt(index);
+    }
+
+    /** Convenience method for indexOf (getNamespace (), name,
+        startIndex). 
+    
+    public int indexOf(String name, int startIndex) {
+        return indexOf(getNamespace(), name, startIndex);
+    }
+    */
+
+    /** Performs search for an element with the given namespace and
+    name, starting at the given start index. A null namespace
+    matches any namespace, please use Xml.NO_NAMESPACE for no
+    namespace).  returns -1 if no matching element was found. */
+
+    public int indexOf(String namespace, String name, int startIndex) {
+
+        int len = getChildCount();
+
+        for (int i = startIndex; i < len; i++) {
+
+            Element child = getElement(i);
+
+            if (child != null
+                && name.equals(child.getName())
+                && (namespace == null || namespace.equals(child.getNamespace())))
+                return i;
+        }
+        return -1;
+    }
+
+    public boolean isText(int i) {
+        int t = getType(i);
+        return t == TEXT || t == IGNORABLE_WHITESPACE || t == CDSECT;
+    }
+
+    /** Recursively builds the child elements from the given parser
+    until an end tag or end document is found. 
+        The end tag is not consumed. */
+
+    public void parse(XmlPullParser parser)
+        throws IOException, XmlPullParserException {
+
+        boolean leave = false;
+
+        do {
+            int type = parser.getEventType();
+            
+   //         System.out.println(parser.getPositionDescription());
+            
+            switch (type) {
+
+                case XmlPullParser.START_TAG :
+                    {
+                        Element child =
+                            createElement(
+                                parser.getNamespace(),
+                                parser.getName());
+                        //    child.setAttributes (event.getAttributes ());
+                        addChild(ELEMENT, child);
+
+                        // order is important here since 
+                        // setparent may perform some init code!
+
+                        child.parse(parser);
+                        break;
+                    }
+
+                case XmlPullParser.END_DOCUMENT :
+                case XmlPullParser.END_TAG :
+                    leave = true;
+                    break;
+
+                default :
+                    if (parser.getText() != null)
+                        addChild(
+                            type == XmlPullParser.ENTITY_REF ? TEXT : type,
+                            parser.getText());
+                    else if (
+                        type == XmlPullParser.ENTITY_REF
+                            && parser.getName() != null) {
+                        addChild(ENTITY_REF, parser.getName());
+                    }
+                    parser.nextToken();
+            }
+        }
+        while (!leave);
+    }
+
+    /** Removes the child object at the given index */
+
+    public void removeChild(int idx) {
+        children.removeElementAt(idx);
+
+        /***  Modification by HHS - start ***/
+        //      types.deleteCharAt (index);
+        /***/
+        int n = types.length() - 1;
+
+        for (int i = idx; i < n; i++)
+            types.setCharAt(i, types.charAt(i + 1));
+
+        types.setLength(n);
+
+        /***  Modification by HHS - end   ***/
+    }
+
+    /* returns a valid XML representation of this Element including
+        attributes and children. 
+    public String toString() {
+        try {
+            ByteArrayOutputStream bos =
+                new ByteArrayOutputStream();
+            XmlWriter xw =
+                new XmlWriter(new OutputStreamWriter(bos));
+            write(xw);
+            xw.close();
+            return new String(bos.toByteArray());
+        }
+        catch (IOException e) {
+            throw new RuntimeException(e.toString());
+        }
+    }
+    */
+
+    /** Writes this node to the given XmlWriter. For node and document,
+        this method is identical to writeChildren, except that the
+        stream is flushed automatically. */
+
+    public void write(XmlSerializer writer) throws IOException {
+        writeChildren(writer);
+        writer.flush();
+    }
+
+    /** Writes the children of this node to the given XmlWriter. */
+
+    public void writeChildren(XmlSerializer writer) throws IOException {
+        if (children == null)
+            return;
+
+        int len = children.size();
+
+        for (int i = 0; i < len; i++) {
+            int type = getType(i);
+            Object child = children.elementAt(i);
+            switch (type) {
+                case ELEMENT :
+                     ((Element) child).write(writer);
+                    break;
+
+                case TEXT :
+                    writer.text((String) child);
+                    break;
+
+                case IGNORABLE_WHITESPACE :
+                    writer.ignorableWhitespace((String) child);
+                    break;
+
+                case CDSECT :
+                    writer.cdsect((String) child);
+                    break;
+
+                case COMMENT :
+                    writer.comment((String) child);
+                    break;
+
+                case ENTITY_REF :
+                    writer.entityRef((String) child);
+                    break;
+
+                case PROCESSING_INSTRUCTION :
+                    writer.processingInstruction((String) child);
+                    break;
+
+                case DOCDECL :
+                    writer.docdecl((String) child);
+                    break;
+
+                default :
+                    throw new RuntimeException("Illegal type: " + type);
+            }
+        }
+    }
+}
diff --git a/xml/src/main/java/org/kxml2/wap/Wbxml.java b/xml/src/main/java/org/kxml2/wap/Wbxml.java
new file mode 100644
index 0000000..5b0c2d3
--- /dev/null
+++ b/xml/src/main/java/org/kxml2/wap/Wbxml.java
@@ -0,0 +1,49 @@
+/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The  above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE. */
+
+package org.kxml2.wap;
+
+
+/** contains the WBXML constants  */
+
+
+public interface Wbxml {
+
+    static public final int SWITCH_PAGE = 0;
+    static public final int END = 1;
+    static public final int ENTITY = 2;
+    static public final int STR_I = 3;
+    static public final int LITERAL = 4;
+    static public final int EXT_I_0 = 0x40;
+    static public final int EXT_I_1 = 0x41;
+    static public final int EXT_I_2 = 0x42;
+    static public final int PI = 0x43;
+    static public final int LITERAL_C = 0x44;
+    static public final int EXT_T_0 = 0x80;
+    static public final int EXT_T_1 = 0x81;
+    static public final int EXT_T_2 = 0x82;
+    static public final int STR_T = 0x83;
+    static public final int LITERAL_A = 0x084;
+    static public final int EXT_0 = 0x0c0;
+    static public final int EXT_1 = 0x0c1;
+    static public final int EXT_2 = 0x0c2;
+    static public final int OPAQUE = 0x0c3; 
+    static public final int LITERAL_AC = 0x0c4;
+}
diff --git a/xml/src/main/java/org/kxml2/wap/WbxmlParser.java b/xml/src/main/java/org/kxml2/wap/WbxmlParser.java
new file mode 100644
index 0000000..617e1d4
--- /dev/null
+++ b/xml/src/main/java/org/kxml2/wap/WbxmlParser.java
@@ -0,0 +1,1075 @@
+/* Copyright (c) 2002,2003,2004 Stefan Haustein, Oberhausen, Rhld., Germany
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The  above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE. */
+
+// Contributors: Bjorn Aadland, Chris Bartley, Nicola Fankhauser,
+//               Victor Havin,  Christian Kurzke, Bogdan Onoiu,
+//                Elias Ross, Jain Sanjay, David Santoro.
+
+package org.kxml2.wap;
+
+import java.io.*;
+import java.util.Vector;
+import java.util.Hashtable;
+
+import org.xmlpull.v1.*;
+
+
+public class WbxmlParser implements XmlPullParser {
+
+    static final String HEX_DIGITS = "0123456789abcdef";
+    
+    /** Parser event type for Wbxml-specific events. The Wbxml event code can be 
+     * accessed with getWapCode() */
+    
+    public static final int WAP_EXTENSION = 64;
+    
+    static final private String UNEXPECTED_EOF =
+    "Unexpected EOF";
+    static final private String ILLEGAL_TYPE =
+    "Wrong event type";
+    
+    private InputStream in;
+    
+    private int TAG_TABLE = 0;
+    private int ATTR_START_TABLE = 1;
+    private int ATTR_VALUE_TABLE = 2;
+    
+    private String[] attrStartTable;
+    private String[] attrValueTable;
+    private String[] tagTable;
+    private byte[] stringTable;
+    private Hashtable cacheStringTable = null;
+    private boolean processNsp;
+    
+    private int depth;
+    private String[] elementStack = new String[16];
+    private String[] nspStack = new String[8];
+    private int[] nspCounts = new int[4];
+    
+    private int attributeCount;
+    private String[] attributes = new String[16];
+    private int nextId = -2;
+    
+    private Vector tables = new Vector();
+    
+    private int version;
+    private int publicIdentifierId;
+    
+    //    StartTag current;
+    //    ParseEvent next;
+    
+    private String prefix;
+    private String namespace;
+    private String name;
+    private String text;
+
+    private Object wapExtensionData;
+    private int wapCode;
+    
+    private int type;
+    
+    private boolean degenerated;
+    private boolean isWhitespace;
+    private String encoding;
+    
+    public boolean getFeature(String feature) {
+        if (XmlPullParser
+        .FEATURE_PROCESS_NAMESPACES
+        .equals(feature))
+            return processNsp;
+        else
+            return false;
+    }
+    
+    public String getInputEncoding() {
+        return encoding;
+    }
+    
+    public void defineEntityReplacementText(
+    String entity,
+    String value)
+    throws XmlPullParserException {
+        
+        // just ignore, has no effect
+    }
+    
+    public Object getProperty(String property) {
+        return null;
+    }
+    
+    public int getNamespaceCount(int depth) {
+        if (depth > this.depth)
+            throw new IndexOutOfBoundsException();
+        return nspCounts[depth];
+    }
+    
+    public String getNamespacePrefix(int pos) {
+        return nspStack[pos << 1];
+    }
+    
+    public String getNamespaceUri(int pos) {
+        return nspStack[(pos << 1) + 1];
+    }
+    
+    public String getNamespace(String prefix) {
+        
+        if ("xml".equals(prefix))
+            return "http://www.w3.org/XML/1998/namespace";
+        if ("xmlns".equals(prefix))
+            return "http://www.w3.org/2000/xmlns/";
+        
+        for (int i = (getNamespaceCount(depth) << 1) - 2;
+        i >= 0;
+        i -= 2) {
+            if (prefix == null) {
+                if (nspStack[i] == null)
+                    return nspStack[i + 1];
+            }
+            else if (prefix.equals(nspStack[i]))
+                return nspStack[i + 1];
+        }
+        return null;
+    }
+    
+    public int getDepth() {
+        return depth;
+    }
+    
+    public String getPositionDescription() {
+        
+        StringBuffer buf =
+        new StringBuffer(
+        type < TYPES.length ? TYPES[type] : "unknown");
+        buf.append(' ');
+        
+        if (type == START_TAG || type == END_TAG) {
+            if (degenerated)
+                buf.append("(empty) ");
+            buf.append('<');
+            if (type == END_TAG)
+                buf.append('/');
+            
+            if (prefix != null)
+                buf.append("{" + namespace + "}" + prefix + ":");
+            buf.append(name);
+            
+            int cnt = attributeCount << 2;
+            for (int i = 0; i < cnt; i += 4) {
+                buf.append(' ');
+                if (attributes[i + 1] != null)
+                    buf.append(
+                    "{"
+                    + attributes[i]
+                    + "}"
+                    + attributes[i
+                    + 1]
+                    + ":");
+                buf.append(
+                attributes[i
+                + 2]
+                + "='"
+                + attributes[i
+                + 3]
+                + "'");
+            }
+            
+            buf.append('>');
+        }
+        else if (type == IGNORABLE_WHITESPACE);
+        else if (type != TEXT)
+            buf.append(getText());
+        else if (isWhitespace)
+            buf.append("(whitespace)");
+        else {
+            String text = getText();
+            if (text.length() > 16)
+                text = text.substring(0, 16) + "...";
+            buf.append(text);
+        }
+        
+        return buf.toString();
+    }
+    
+    public int getLineNumber() {
+        return -1;
+    }
+    
+    public int getColumnNumber() {
+        return -1;
+    }
+    
+    public boolean isWhitespace()
+    throws XmlPullParserException {
+        if (type != TEXT
+        && type != IGNORABLE_WHITESPACE
+        && type != CDSECT)
+            exception(ILLEGAL_TYPE);
+        return isWhitespace;
+    }
+    
+    public String getText() {
+        return text;
+    }
+    
+    public char[] getTextCharacters(int[] poslen) {
+        if (type >= TEXT) {
+            poslen[0] = 0;
+            poslen[1] = text.length();
+            char[] buf = new char[text.length()];
+            text.getChars(0, text.length(), buf, 0);
+            return buf;
+        }
+        
+        poslen[0] = -1;
+        poslen[1] = -1;
+        return null;
+    }
+    
+    public String getNamespace() {
+        return namespace;
+    }
+    
+    public String getName() {
+        return name;
+    }
+    
+    public String getPrefix() {
+        return prefix;
+    }
+    
+    public boolean isEmptyElementTag()
+    throws XmlPullParserException {
+        if (type != START_TAG)
+            exception(ILLEGAL_TYPE);
+        return degenerated;
+    }
+    
+    public int getAttributeCount() {
+        return attributeCount;
+    }
+    
+    public String getAttributeType(int index) {
+        return "CDATA";
+    }
+    
+    public boolean isAttributeDefault(int index) {
+        return false;
+    }
+    
+    public String getAttributeNamespace(int index) {
+        if (index >= attributeCount)
+            throw new IndexOutOfBoundsException();
+        return attributes[index << 2];
+    }
+    
+    public String getAttributeName(int index) {
+        if (index >= attributeCount)
+            throw new IndexOutOfBoundsException();
+        return attributes[(index << 2) + 2];
+    }
+    
+    public String getAttributePrefix(int index) {
+        if (index >= attributeCount)
+            throw new IndexOutOfBoundsException();
+        return attributes[(index << 2) + 1];
+    }
+    
+    public String getAttributeValue(int index) {
+        if (index >= attributeCount)
+            throw new IndexOutOfBoundsException();
+        return attributes[(index << 2) + 3];
+    }
+    
+    public String getAttributeValue(
+    String namespace,
+    String name) {
+        
+        for (int i = (attributeCount << 2) - 4;
+        i >= 0;
+        i -= 4) {
+            if (attributes[i + 2].equals(name)
+            && (namespace == null
+            || attributes[i].equals(namespace)))
+                return attributes[i + 3];
+        }
+        
+        return null;
+    }
+    
+    public int getEventType() throws XmlPullParserException {
+        return type;
+    }
+    
+    
+    // TODO: Reuse resolveWapExtension here? Raw Wap extensions would still be accessible
+    // via nextToken();  ....?
+    
+    public int next() throws XmlPullParserException, IOException {
+        
+        isWhitespace = true;
+        int minType = 9999;
+        
+        while (true) {
+            
+            String save = text;
+            
+            nextImpl();
+            
+            if (type < minType)
+                minType = type;
+            
+            if (minType > CDSECT) continue; // no "real" event so far
+            
+            if (minType >= TEXT) {  // text, see if accumulate
+                
+                if (save != null) text = text == null ? save : save + text;
+                
+                switch(peekId()) {
+                    case Wbxml.ENTITY:
+                    case Wbxml.STR_I:
+                    case Wbxml.STR_T:
+                    case Wbxml.LITERAL:
+                    case Wbxml.LITERAL_C:
+                    case Wbxml.LITERAL_A:
+                    case Wbxml.LITERAL_AC: continue;
+                }
+            }
+            
+            break;
+        }
+        
+        type = minType;
+        
+        if (type > TEXT)
+            type = TEXT;
+        
+        return type;
+    }
+    
+    
+    public int nextToken() throws XmlPullParserException, IOException {
+        
+        isWhitespace = true;
+        nextImpl();
+        return type;
+    }
+    
+    
+    
+    public int nextTag() throws XmlPullParserException, IOException {
+        
+        next();
+        if (type == TEXT && isWhitespace)
+            next();
+        
+        if (type != END_TAG && type != START_TAG)
+            exception("unexpected type");
+        
+        return type;
+    }
+    
+    
+    public String nextText() throws XmlPullParserException, IOException {
+        if (type != START_TAG)
+            exception("precondition: START_TAG");
+        
+        next();
+        
+        String result;
+        
+        if (type == TEXT) {
+            result = getText();
+            next();
+        }
+        else
+            result = "";
+        
+        if (type != END_TAG)
+            exception("END_TAG expected");
+        
+        return result;
+    }
+    
+    
+    public void require(int type, String namespace, String name)
+    throws XmlPullParserException, IOException {
+        
+        if (type != this.type
+        || (namespace != null && !namespace.equals(getNamespace()))
+        || (name != null && !name.equals(getName())))
+            exception(
+            "expected: " + (type == WAP_EXTENSION ? "WAP Ext." : (TYPES[type] + " {" + namespace + "}" + name)));
+    }
+    
+    
+    public void setInput(Reader reader) throws XmlPullParserException {
+        exception("InputStream required");
+    }
+    
+    public void setInput(InputStream in, String enc)
+    throws XmlPullParserException {
+        
+        this.in = in;
+        
+        try {
+            version = readByte();
+            publicIdentifierId = readInt();
+            
+            if (publicIdentifierId == 0)
+                readInt();
+            
+            int charset = readInt(); // skip charset
+            
+            if (null == enc){
+                switch (charset){
+                    case   4: encoding = "ISO-8859-1"; break;
+                    case 106: encoding = "UTF-8";      break;
+                    // add more if you need them
+                    // http://www.iana.org/assignments/character-sets
+                    // case MIBenum: encoding = Name  break;
+                    default:  throw new UnsupportedEncodingException(""+charset);
+                } 
+            }else{
+                encoding = enc;
+            }
+
+            int strTabSize = readInt();
+            stringTable = new byte[strTabSize];
+            
+            int ok = 0;
+            while(ok < strTabSize){
+                int cnt = in.read(stringTable, ok, strTabSize - ok);
+                if(cnt <= 0) break;
+                ok += cnt;
+            }
+            
+            selectPage(0, true);
+            selectPage(0, false);
+        }
+        catch (IOException e) {
+            exception("Illegal input format");
+        }
+    }
+    
+    public void setFeature(String feature, boolean value)
+    throws XmlPullParserException {
+        if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature))
+            processNsp = value;
+        else
+            exception("unsupported feature: " + feature);
+    }
+    
+    public void setProperty(String property, Object value)
+    throws XmlPullParserException {
+        throw new XmlPullParserException("unsupported property: " + property);
+    }
+    
+    // ---------------------- private / internal methods
+    
+    private final boolean adjustNsp()
+    throws XmlPullParserException {
+        
+        boolean any = false;
+        
+        for (int i = 0; i < attributeCount << 2; i += 4) {
+            // * 4 - 4; i >= 0; i -= 4) {
+            
+            String attrName = attributes[i + 2];
+            int cut = attrName.indexOf(':');
+            String prefix;
+            
+            if (cut != -1) {
+                prefix = attrName.substring(0, cut);
+                attrName = attrName.substring(cut + 1);
+            }
+            else if (attrName.equals("xmlns")) {
+                prefix = attrName;
+                attrName = null;
+            }
+            else
+                continue;
+            
+            if (!prefix.equals("xmlns")) {
+                any = true;
+            }
+            else {
+                int j = (nspCounts[depth]++) << 1;
+                
+                nspStack = ensureCapacity(nspStack, j + 2);
+                nspStack[j] = attrName;
+                nspStack[j + 1] = attributes[i + 3];
+                
+                if (attrName != null
+                && attributes[i + 3].equals(""))
+                    exception("illegal empty namespace");
+                
+                //  prefixMap = new PrefixMap (prefixMap, attrName, attr.getValue ());
+                
+                //System.out.println (prefixMap);
+                
+                System.arraycopy(
+                attributes,
+                i + 4,
+                attributes,
+                i,
+                ((--attributeCount) << 2) - i);
+                
+                i -= 4;
+            }
+        }
+        
+        if (any) {
+            for (int i = (attributeCount << 2) - 4;
+            i >= 0;
+            i -= 4) {
+                
+                String attrName = attributes[i + 2];
+                int cut = attrName.indexOf(':');
+                
+                if (cut == 0)
+                    throw new RuntimeException(
+                    "illegal attribute name: "
+                    + attrName
+                    + " at "
+                    + this);
+                
+                else if (cut != -1) {
+                    String attrPrefix =
+                    attrName.substring(0, cut);
+                    
+                    attrName = attrName.substring(cut + 1);
+                    
+                    String attrNs = getNamespace(attrPrefix);
+                    
+                    if (attrNs == null)
+                        throw new RuntimeException(
+                        "Undefined Prefix: "
+                        + attrPrefix
+                        + " in "
+                        + this);
+                    
+                    attributes[i] = attrNs;
+                    attributes[i + 1] = attrPrefix;
+                    attributes[i + 2] = attrName;
+                    
+                    for (int j = (attributeCount << 2) - 4;
+                    j > i;
+                    j -= 4)
+                        if (attrName.equals(attributes[j + 2])
+                        && attrNs.equals(attributes[j]))
+                            exception(
+                            "Duplicate Attribute: {"
+                            + attrNs
+                            + "}"
+                            + attrName);
+                }
+            }
+        }
+        
+        int cut = name.indexOf(':');
+        
+        if (cut == 0)
+            exception("illegal tag name: " + name);
+        else if (cut != -1) {
+            prefix = name.substring(0, cut);
+            name = name.substring(cut + 1);
+        }
+        
+        this.namespace = getNamespace(prefix);
+        
+        if (this.namespace == null) {
+            if (prefix != null)
+                exception("undefined prefix: " + prefix);
+            this.namespace = NO_NAMESPACE;
+        }
+        
+        return any;
+    }
+    
+    private final void setTable(int page, int type, String[] table) {
+        if(stringTable != null){
+            throw new RuntimeException("setXxxTable must be called before setInput!");
+        }
+        while(tables.size() < 3*page +3){
+            tables.addElement(null);
+        }
+        tables.setElementAt(table, page*3+type);
+    }
+    
+    
+    
+    
+    
+    private final void exception(String desc)
+    throws XmlPullParserException {
+        throw new XmlPullParserException(desc, this, null);
+    }
+    
+    
+    private void selectPage(int nr, boolean tags) throws XmlPullParserException{
+        if(tables.size() == 0 && nr == 0) return;
+        
+        if(nr*3 > tables.size())
+            exception("Code Page "+nr+" undefined!");
+        
+        if(tags)
+            tagTable = (String[]) tables.elementAt(nr * 3 + TAG_TABLE);
+        else {
+            attrStartTable = (String[]) tables.elementAt(nr * 3 + ATTR_START_TABLE);
+            attrValueTable = (String[]) tables.elementAt(nr * 3 + ATTR_VALUE_TABLE);
+        }
+    }
+    
+    private final void nextImpl()
+    throws IOException, XmlPullParserException {
+        
+        String s;
+        
+        if (type == END_TAG) {
+            depth--;
+        }
+        
+        if (degenerated) {
+            type = XmlPullParser.END_TAG;
+            degenerated = false;
+            return;
+        }
+        
+        text = null;
+        prefix = null;
+        name = null;
+        
+        int id = peekId ();
+        while(id == Wbxml.SWITCH_PAGE){
+            nextId = -2;
+            selectPage(readByte(), true);
+            id = peekId();
+        }
+        nextId = -2;
+        
+        switch (id) {
+            case -1 :
+                type = XmlPullParser.END_DOCUMENT;
+                break;
+                
+            case Wbxml.END : 
+            {
+                int sp = (depth - 1) << 2;
+                
+                type = END_TAG;
+                namespace = elementStack[sp];
+                prefix = elementStack[sp + 1];
+                name = elementStack[sp + 2];
+            }
+            break;
+            
+            case Wbxml.ENTITY : 
+            {
+                type = ENTITY_REF;
+                char c = (char) readInt();
+                text = "" + c;
+                name = "#" + ((int) c);
+            }
+            
+            break;
+            
+            case Wbxml.STR_I :
+                type = TEXT;
+                text = readStrI();
+                break;
+                
+            case Wbxml.EXT_I_0 :
+            case Wbxml.EXT_I_1 :
+            case Wbxml.EXT_I_2 :
+            case Wbxml.EXT_T_0 :
+            case Wbxml.EXT_T_1 :
+            case Wbxml.EXT_T_2 :
+            case Wbxml.EXT_0 :
+            case Wbxml.EXT_1 :
+            case Wbxml.EXT_2 :
+            case Wbxml.OPAQUE :
+                
+                type = WAP_EXTENSION;
+                wapCode = id;
+                wapExtensionData = parseWapExtension(id);
+                break;
+                
+            case Wbxml.PI :
+                throw new RuntimeException("PI curr. not supp.");
+                // readPI;
+                // break;
+                
+            case Wbxml.STR_T : 
+            {
+                type = TEXT;
+                text = readStrT();
+            }
+            break;
+            
+            default :
+                parseElement(id);
+        }
+        //        }
+        //      while (next == null);
+        
+        //        return next;
+    }
+    
+    /** Overwrite this method to intercept all wap events */
+    
+    public Object parseWapExtension(int id)  throws IOException, XmlPullParserException {
+        
+        switch (id) {
+            case Wbxml.EXT_I_0 :
+            case Wbxml.EXT_I_1 :
+            case Wbxml.EXT_I_2 :
+                return readStrI();
+                
+            case Wbxml.EXT_T_0 :
+            case Wbxml.EXT_T_1 :
+            case Wbxml.EXT_T_2 :
+                return new Integer(readInt());
+                
+            case Wbxml.EXT_0 :
+            case Wbxml.EXT_1 :
+            case Wbxml.EXT_2 :
+                return null;
+                
+            case Wbxml.OPAQUE : 
+            {
+                int count = readInt();
+                byte[] buf = new byte[count];
+                
+                while(count > 0){
+                    count -= in.read(buf, buf.length-count, count);
+                }
+                
+                return buf;
+            } // case OPAQUE
+    
+            
+            default:
+                exception("illegal id: "+id);
+                return null; // dead code
+        } // SWITCH
+    }
+    
+    public void readAttr() throws IOException, XmlPullParserException {
+        
+        int id = readByte();
+        int i = 0;
+        
+        while (id != 1) {
+            
+            while(id == Wbxml.SWITCH_PAGE){
+                selectPage(readByte(), false);
+                id = readByte();
+            }
+            
+            String name = resolveId(attrStartTable, id);
+            StringBuffer value;
+            
+            int cut = name.indexOf('=');
+            
+            if (cut == -1)
+                value = new StringBuffer();
+            else {
+                value =
+                new StringBuffer(name.substring(cut + 1));
+                name = name.substring(0, cut);
+            }
+            
+            id = readByte();
+            while (id > 128
+            || id == Wbxml.SWITCH_PAGE
+            || id == Wbxml.ENTITY
+            || id == Wbxml.STR_I
+            || id == Wbxml.STR_T
+            || (id >= Wbxml.EXT_I_0 && id <= Wbxml.EXT_I_2)
+            || (id >= Wbxml.EXT_T_0 && id <= Wbxml.EXT_T_2)) {
+                
+                switch (id) {
+                    case Wbxml.SWITCH_PAGE :
+                        selectPage(readByte(), false);
+                        break;
+                        
+                    case Wbxml.ENTITY :
+                        value.append((char) readInt());
+                        break;
+                        
+                    case Wbxml.STR_I :
+                        value.append(readStrI());
+                        break;
+                        
+                    case Wbxml.EXT_I_0 :
+                    case Wbxml.EXT_I_1 :
+                    case Wbxml.EXT_I_2 :
+                    case Wbxml.EXT_T_0 :
+                    case Wbxml.EXT_T_1 :
+                    case Wbxml.EXT_T_2 :
+                    case Wbxml.EXT_0 :
+                    case Wbxml.EXT_1 :
+                    case Wbxml.EXT_2 :
+                    case Wbxml.OPAQUE :
+                        value.append(resolveWapExtension(id, parseWapExtension(id)));
+                        break;
+                        
+                    case Wbxml.STR_T :
+                        value.append(readStrT());
+                        break;
+                        
+                    default :
+                        value.append(
+                        resolveId(attrValueTable, id));
+                }
+                
+                id = readByte();
+            }
+            
+            attributes = ensureCapacity(attributes, i + 4);
+            
+            attributes[i++] = "";
+            attributes[i++] = null;
+            attributes[i++] = name;
+            attributes[i++] = value.toString();
+            
+            attributeCount++;
+        }
+    }
+    
+    private int peekId () throws IOException {
+        if (nextId == -2) {
+            nextId = in.read ();
+        }
+        return nextId;
+    }
+    
+    /** overwrite for own WAP extension handling in attributes and high level parsing 
+     * (above nextToken() level) */
+    
+    protected String resolveWapExtension(int id, Object data){
+        
+        if(data instanceof byte[]){
+            StringBuffer sb = new StringBuffer();
+            byte[] b = (byte[]) data;
+            
+            for (int i = 0; i < b.length; i++) {
+                sb.append(HEX_DIGITS.charAt((b[i] >> 4) & 0x0f));
+                sb.append(HEX_DIGITS.charAt(b[i] & 0x0f));
+            }
+            return sb.toString();
+        }
+
+        return "$("+data+")";
+    }
+    
+    String resolveId(String[] tab, int id) throws IOException {
+        int idx = (id & 0x07f) - 5;
+        if (idx == -1){
+            wapCode = -1;
+            return readStrT();
+        }
+        if (idx < 0
+        || tab == null
+        || idx >= tab.length
+        || tab[idx] == null)
+            throw new IOException("id " + id + " undef.");
+        
+        wapCode = idx+5;
+        
+        return tab[idx];
+    }
+    
+    void parseElement(int id)
+    throws IOException, XmlPullParserException {
+        
+        type = START_TAG;
+        name = resolveId(tagTable, id & 0x03f);
+        
+        attributeCount = 0;
+        if ((id & 128) != 0) {
+            readAttr();
+        }
+        
+        degenerated = (id & 64) == 0;
+        
+        int sp = depth++ << 2;
+        
+        // transfer to element stack
+        
+        elementStack = ensureCapacity(elementStack, sp + 4);
+        elementStack[sp + 3] = name;
+        
+        if (depth >= nspCounts.length) {
+            int[] bigger = new int[depth + 4];
+            System.arraycopy(nspCounts, 0, bigger, 0, nspCounts.length);
+            nspCounts = bigger;
+        }
+        
+        nspCounts[depth] = nspCounts[depth - 1];
+        
+        for (int i = attributeCount - 1; i > 0; i--) {
+            for (int j = 0; j < i; j++) {
+                if (getAttributeName(i)
+                .equals(getAttributeName(j)))
+                    exception(
+                    "Duplicate Attribute: "
+                    + getAttributeName(i));
+            }
+        }
+        
+        if (processNsp)
+            adjustNsp();
+        else
+            namespace = "";
+        
+        elementStack[sp] = namespace;
+        elementStack[sp + 1] = prefix;
+        elementStack[sp + 2] = name;
+        
+    }
+    
+    private final String[] ensureCapacity(
+    String[] arr,
+    int required) {
+        if (arr.length >= required)
+            return arr;
+        String[] bigger = new String[required + 16];
+        System.arraycopy(arr, 0, bigger, 0, arr.length);
+        return bigger;
+    }
+    
+    int readByte() throws IOException {
+        int i = in.read();
+        if (i == -1)
+            throw new IOException("Unexpected EOF");
+        return i;
+    }
+    
+    int readInt() throws IOException {
+        int result = 0;
+        int i;
+        
+        do {
+            i = readByte();
+            result = (result << 7) | (i & 0x7f);
+        }
+        while ((i & 0x80) != 0);
+        
+        return result;
+    }
+    
+    String readStrI() throws IOException {
+        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+        boolean wsp = true;
+        while (true){
+            int i = in.read();
+            if (i == 0){
+                break;
+            }
+            if (i == -1){
+                throw new IOException(UNEXPECTED_EOF);
+            }
+            if (i > 32){
+                wsp = false;
+            }
+            buf.write(i);
+        }
+        isWhitespace = wsp;
+        String result = new String(buf.toByteArray(), encoding);
+        buf.close();
+        return result;
+    }
+    
+    String readStrT() throws IOException {
+        int pos = readInt();
+        // As the main reason of stringTable is compression we build a cache of Strings
+        // stringTable is supposed to help create Strings from parts which means some cache hit rate
+        // This will help to minimize the Strings created when invoking readStrT() repeatedly
+        if (cacheStringTable == null){
+            //Lazy init if device is not using StringTable but inline 0x03 strings
+            cacheStringTable = new Hashtable();
+        }
+        String forReturn = (String) cacheStringTable.get(new Integer(pos));
+        if (forReturn == null){
+
+            int end = pos;
+            while(end < stringTable.length && stringTable[end] != '\0'){
+                end++;
+            }
+            forReturn = new String(stringTable, pos, end-pos, encoding);
+            cacheStringTable.put(new Integer(pos), forReturn);
+        }
+        return forReturn;
+    }
+    
+    /**
+     * Sets the tag table for a given page.
+     * The first string in the array defines tag 5, the second tag 6 etc.
+     */
+    
+    public void setTagTable(int page, String[] table) {
+        setTable(page, TAG_TABLE, table);
+        
+        //        this.tagTable = tagTable;
+        //      if (page != 0)
+        //        throw new RuntimeException("code pages curr. not supp.");
+    }
+    
+    /** Sets the attribute start Table for a given page.
+     *  The first string in the array defines attribute
+     *  5, the second attribute 6 etc. Please use the
+     *  character '=' (without quote!) as delimiter
+     *  between the attribute name and the (start of the) value
+     */
+    
+    public void setAttrStartTable(
+    int page,
+    String[] table) {
+        
+        setTable(page, ATTR_START_TABLE, table);
+    }
+    
+    /** Sets the attribute value Table for a given page.
+     *  The first string in the array defines attribute value 0x85,
+     *  the second attribute value 0x86 etc.
+     */
+    
+    public void setAttrValueTable(
+    int page,
+    String[] table) {
+        
+        setTable(page, ATTR_VALUE_TABLE, table);
+    }
+    
+    /** Returns the token ID for start tags or the event type for wap proprietary events
+     * such as OPAQUE.
+     */
+    
+    public int getWapCode(){
+        return wapCode;
+    }
+    
+    public Object getWapExtensionData(){
+        return wapExtensionData;
+    }
+    
+    
+}
diff --git a/xml/src/main/java/org/kxml2/wap/WbxmlSerializer.java b/xml/src/main/java/org/kxml2/wap/WbxmlSerializer.java
new file mode 100644
index 0000000..8c1b598
--- /dev/null
+++ b/xml/src/main/java/org/kxml2/wap/WbxmlSerializer.java
@@ -0,0 +1,512 @@
+/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ * sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The  above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ * IN THE SOFTWARE. */
+
+//Contributors: Jonathan Cox, Bogdan Onoiu, Jerry Tian
+
+package org.kxml2.wap;
+
+import java.io.*;
+import java.util.*;
+
+import org.xmlpull.v1.*;
+
+// TODO: make some of the "direct" WBXML token writing methods public??
+
+/**
+ * A class for writing WBXML.
+ *
+ */
+
+
+
+public class WbxmlSerializer implements XmlSerializer {
+    
+    
+    Hashtable stringTable = new Hashtable();
+    
+    OutputStream out;
+    
+    ByteArrayOutputStream buf = new ByteArrayOutputStream();
+    ByteArrayOutputStream stringTableBuf = new ByteArrayOutputStream();
+    
+    String pending;
+    int depth;
+    String name;
+    String namespace;
+    Vector attributes = new Vector();
+    
+    Hashtable attrStartTable = new Hashtable();
+    Hashtable attrValueTable = new Hashtable();
+    Hashtable tagTable = new Hashtable();
+    
+    private int attrPage;
+    private int tagPage;
+    
+    private String encoding;
+    
+    
+    public XmlSerializer attribute(String namespace, String name, String value) {
+        attributes.addElement(name);
+        attributes.addElement(value);
+        return this;
+    }
+    
+    
+    public void cdsect (String cdsect) throws IOException{
+        text (cdsect);
+    }
+    
+    
+    
+    /* silently ignore comment */
+    
+    public void comment (String comment) {
+    }
+    
+    
+    public void docdecl (String docdecl) {
+        throw new RuntimeException ("Cannot write docdecl for WBXML");
+    }
+    
+    
+    public void entityRef (String er) {
+        throw new RuntimeException ("EntityReference not supported for WBXML");
+    }
+    
+    public int getDepth() {
+        return depth;
+    }
+    
+    
+    public boolean getFeature (String name) {
+        return false;
+    }
+    
+    public String getNamespace() {
+        throw new RuntimeException("NYI");
+    }
+    
+    public String getName() {
+        throw new RuntimeException("NYI");
+    }
+    
+    public String getPrefix(String nsp, boolean create) {
+        throw new RuntimeException ("NYI");
+    }
+    
+    
+    public Object getProperty (String name) {
+        return null;
+    }
+    
+    public void ignorableWhitespace (String sp) {
+    }
+    
+    
+    public void endDocument() throws IOException {
+        writeInt(out, stringTableBuf.size());
+        
+        // write StringTable
+        
+        out.write(stringTableBuf.toByteArray());
+        
+        // write buf
+        
+        out.write(buf.toByteArray());
+        
+        // ready!
+        
+        out.flush();
+    }
+    
+    
+    /** ATTENTION: flush cannot work since Wbxml documents require
+      buffering. Thus, this call does nothing. */
+    
+    public void flush() {
+    }
+    
+    
+    public void checkPending(boolean degenerated) throws IOException {
+        if (pending == null)
+            return;
+        
+        int len = attributes.size();
+        
+        int[] idx = (int[]) tagTable.get(pending);
+        
+        // if no entry in known table, then add as literal
+        if (idx == null) {
+            buf.write(
+            len == 0
+            ? (degenerated ? Wbxml.LITERAL : Wbxml.LITERAL_C)
+            : (degenerated ? Wbxml.LITERAL_A : Wbxml.LITERAL_AC));
+            
+            writeStrT(pending, false);
+        }
+        else {
+            if(idx[0] != tagPage){
+                tagPage=idx[0];
+                buf.write(Wbxml.SWITCH_PAGE);
+                buf.write(tagPage);
+            }
+            
+            buf.write(
+            len == 0
+            ? (degenerated ? idx[1] : idx[1] | 64)
+            : (degenerated
+            ? idx[1] | 128
+            : idx[1] | 192));
+           
+        }
+        
+        for (int i = 0; i < len;) {
+            idx = (int[]) attrStartTable.get(attributes.elementAt(i));
+            
+            if (idx == null) {
+                buf.write(Wbxml.LITERAL);
+                writeStrT((String) attributes.elementAt(i), false);
+            }
+            else {
+                if(idx[0] != attrPage){
+                        attrPage = idx[0];
+                    buf.write(0);
+                    buf.write(attrPage);
+                }
+                buf.write(idx[1]);
+            }
+            idx = (int[]) attrValueTable.get(attributes.elementAt(++i));
+            if (idx == null) {
+                writeStr((String) attributes.elementAt(i));
+            }
+            else {
+                if(idx[0] != attrPage){
+                        attrPage = idx[0];
+                    buf.write(0);
+                    buf.write(attrPage);                    
+                }
+                buf.write(idx[1]);
+            }
+            ++i;
+        }
+        
+        if (len > 0)
+            buf.write(Wbxml.END);
+        
+        pending = null;
+        attributes.removeAllElements();
+    }
+    
+    
+    public void processingInstruction(String pi) {
+        throw new RuntimeException ("PI NYI");
+    }
+    
+    
+    public void setFeature(String name, boolean value) {
+        throw new IllegalArgumentException ("unknown feature "+name);
+    }
+    
+    
+    
+    public void setOutput (Writer writer) {
+        throw new RuntimeException ("Wbxml requires an OutputStream!");
+    }
+    
+    public void setOutput (OutputStream out, String encoding) throws IOException {
+        
+        this.encoding = encoding == null ? "UTF-8" : encoding;
+        this.out = out;
+        
+        buf = new ByteArrayOutputStream();
+        stringTableBuf = new ByteArrayOutputStream();
+        
+        // ok, write header
+    }
+    
+    
+    public void setPrefix(String prefix, String nsp) {
+        throw new RuntimeException("NYI");
+    }
+    
+    public void setProperty(String property, Object value) {
+        throw new IllegalArgumentException ("unknown property "+property);
+    }
+    
+    
+    public void startDocument(String s, Boolean b) throws IOException{
+        out.write(0x03); // version 1.3
+        // http://www.openmobilealliance.org/tech/omna/omna-wbxml-public-docid.htm
+        out.write(0x01); // unknown or missing public identifier
+
+        // default encoding is UTF-8
+        
+        if(s != null){
+            encoding = s;
+        }
+        
+        if (encoding.toUpperCase().equals("UTF-8")){
+            out.write(106);
+        }else if (encoding.toUpperCase().equals("ISO-8859-1")){
+            out.write(0x04);
+        }else{
+            throw new UnsupportedEncodingException(s);
+        }
+    }
+    
+    
+    public XmlSerializer startTag(String namespace, String name) throws IOException {
+        
+        if (namespace != null && !"".equals(namespace))
+            throw new RuntimeException ("NSP NYI");
+        
+        //current = new State(current, prefixMap, name);
+        
+        checkPending(false);
+        pending = name;
+        depth++;
+        
+        return this;
+    }
+    
+    public XmlSerializer text(char[] chars, int start, int len) throws IOException {
+
+        checkPending(false);
+        
+        writeStr(new String(chars, start, len));
+        
+        return this;
+    }
+    
+    public XmlSerializer text(String text) throws IOException {
+        
+        checkPending(false);
+        
+        writeStr(text);
+    
+        return this;
+    }
+    
+
+    /** Used in text() and attribute() to write text */
+    
+    private void writeStr(String text) throws IOException{
+        int p0 = 0;
+        int lastCut = 0;
+        int len = text.length();
+        
+        while(p0 < len){
+            while(p0 < len && text.charAt(p0) < 'A' ){ // skip interpunctation
+                p0++;
+            }
+            int p1 = p0;
+            while(p1 < len && text.charAt(p1) >= 'A'){
+                p1++;
+            }
+            
+            if(p1 - p0 > 10) {
+
+                if(p0 > lastCut && text.charAt(p0-1) == ' ' 
+                    && stringTable.get(text.substring(p0, p1)) == null){
+                    
+                       buf.write(Wbxml.STR_T);
+                       writeStrT(text.substring(lastCut, p1), false);
+                }
+                else {
+
+                    if(p0 > lastCut && text.charAt(p0-1) == ' '){
+                        p0--;
+                    }
+
+                    if(p0 > lastCut){
+                        buf.write(Wbxml.STR_T);
+                        writeStrT(text.substring(lastCut, p0), false);
+                    }
+                    buf.write(Wbxml.STR_T);
+                    writeStrT(text.substring(p0, p1), true);
+                }
+                lastCut = p1;
+            }
+            p0 = p1;
+        }
+
+        if(lastCut < len){
+            buf.write(Wbxml.STR_T);
+            writeStrT(text.substring(lastCut, len), false);
+        }
+    }
+    
+    
+
+    public XmlSerializer endTag(String namespace, String name) throws IOException {
+        
+        //        current = current.prev;
+        
+        if (pending != null)
+            checkPending(true);
+        else
+            buf.write(Wbxml.END);
+        
+        depth--;
+        
+        return this;
+    }
+    
+    /** 
+     * @throws IOException */
+    
+    public void writeWapExtension(int type, Object data) throws IOException {
+        checkPending(false);
+        buf.write(type);
+        switch(type){
+        case Wbxml.EXT_0:
+        case Wbxml.EXT_1:
+        case Wbxml.EXT_2:
+            break;
+        
+        case Wbxml.OPAQUE:
+            byte[] bytes = (byte[]) data;
+            writeInt(buf, bytes.length);
+            buf.write(bytes);
+            break;
+            
+        case Wbxml.EXT_I_0:
+        case Wbxml.EXT_I_1:
+        case Wbxml.EXT_I_2:
+            writeStrI(buf, (String) data);
+            break;
+
+        case Wbxml.EXT_T_0:
+        case Wbxml.EXT_T_1:
+        case Wbxml.EXT_T_2:
+            writeStrT((String) data, false);
+            break;
+            
+        default: 
+            throw new IllegalArgumentException();
+        }
+    }
+    
+    // ------------- internal methods --------------------------
+    
+    static void writeInt(OutputStream out, int i) throws IOException {
+        byte[] buf = new byte[5];
+        int idx = 0;
+        
+        do {
+            buf[idx++] = (byte) (i & 0x7f);
+            i = i >> 7;
+        }
+        while (i != 0);
+        
+        while (idx > 1) {
+            out.write(buf[--idx] | 0x80);
+        }
+        out.write(buf[0]);
+    }
+    
+    void writeStrI(OutputStream out, String s) throws IOException {
+        byte[] data = s.getBytes(encoding);
+        out.write(data);
+        out.write(0);
+    }
+    
+    private final void writeStrT(String s, boolean mayPrependSpace) throws IOException {
+        
+        Integer idx = (Integer) stringTable.get(s);
+        
+        if (idx != null) {
+            writeInt(buf, idx.intValue());
+        }
+        else{
+            int i = stringTableBuf.size();
+            if(s.charAt(0) >= '0' && mayPrependSpace){
+                s = ' ' + s;
+                writeInt(buf, i+1);
+            }
+            else{
+                writeInt(buf, i);
+            }
+            
+               stringTable.put(s, new Integer(i));
+               if(s.charAt(0) == ' '){
+                   stringTable.put(s.substring(1), new Integer(i+1));
+               }
+               int j = s.lastIndexOf(' ');
+               if(j > 1){
+                   stringTable.put(s.substring(j), new Integer(i+j));
+                   stringTable.put(s.substring(j+1), new Integer(i+j+1));
+               }
+                
+            writeStrI(stringTableBuf, s);
+            stringTableBuf.flush();
+        }
+        
+    }
+    
+    /**
+     * Sets the tag table for a given page.
+     * The first string in the array defines tag 5, the second tag 6 etc.
+     */
+    
+    public void setTagTable(int page, String[] tagTable) {
+        // TODO: clear entries in tagTable?
+        
+        for (int i = 0; i < tagTable.length; i++) {
+            if (tagTable[i] != null) {
+                Object idx = new int[]{page, i+5};
+                this.tagTable.put(tagTable[i], idx);
+            }
+        }
+    }
+    
+    /**
+     * Sets the attribute start Table for a given page.
+     * The first string in the array defines attribute
+     * 5, the second attribute 6 etc.
+     *  Please use the
+     *  character '=' (without quote!) as delimiter
+     *  between the attribute name and the (start of the) value
+     */
+    public void setAttrStartTable(int page, String[] attrStartTable) {
+        
+        for (int i = 0; i < attrStartTable.length; i++) {
+            if (attrStartTable[i] != null) {
+                Object idx = new int[] {page, i + 5};
+                this.attrStartTable.put(attrStartTable[i], idx);
+            }
+        }
+    }
+    
+    /**
+     * Sets the attribute value Table for a given page.
+     * The first string in the array defines attribute value 0x85,
+     * the second attribute value 0x86 etc.
+     */
+    public void setAttrValueTable(int page, String[] attrValueTable) {
+        // clear entries in this.table!
+        for (int i = 0; i < attrValueTable.length; i++) {
+            if (attrValueTable[i] != null) {
+                Object idx = new int[]{page, i + 0x085};
+                this.attrValueTable.put(attrValueTable[i], idx);
+            }
+        }
+    }
+}
diff --git a/xml/src/main/java/org/kxml2/wap/syncml/SyncML.java b/xml/src/main/java/org/kxml2/wap/syncml/SyncML.java
new file mode 100644
index 0000000..5ea8496
--- /dev/null
+++ b/xml/src/main/java/org/kxml2/wap/syncml/SyncML.java
@@ -0,0 +1,192 @@
+package org.kxml2.wap.syncml;
+
+import org.kxml2.wap.*;
+
+public abstract class SyncML {
+    
+    
+    // SyncML-Common (-//SYNCML//DTD SyncML 1.2//EN and -//SYNCML//DTD MetInf 1.2//EN) support
+    
+    public static WbxmlParser createParser() {
+        WbxmlParser p = new WbxmlParser();
+        p.setTagTable(0, TAG_TABLE_0);
+        p.setTagTable(1, TAG_TABLE_1);
+        return p;
+    }
+
+    public static WbxmlSerializer createSerializer() {
+        WbxmlSerializer s = new WbxmlSerializer();
+        s.setTagTable(0, TAG_TABLE_0);
+        s.setTagTable(1, TAG_TABLE_1);
+        return s;
+    }
+    
+    
+    // SyncML-Common + DMDDF (-//OMA//DTD-DM-DDF 1.2//EN) support
+    
+    public static WbxmlParser createDMParser() {
+        WbxmlParser p = createParser();
+        p.setTagTable(2, TAG_TABLE_2_DM);
+        return p;
+    }
+
+    public static WbxmlSerializer createDMSerializer() {
+        WbxmlSerializer s = createSerializer();
+        s.setTagTable(2, TAG_TABLE_2_DM);
+        return s;
+    }
+
+    // Tables
+    
+    public static final String [] TAG_TABLE_0 = {
+        
+         //  -//SYNCML//DTD SyncML 1.2//EN
+        
+         "Add",            // 0x05 
+         "Alert",          // 0x06 
+         "Archive",        // 0x07 
+         "Atomic",         // 0x08 
+         "Chal",           // 0x09 
+         "Cmd",            // 0x0a 
+         "CmdID",          // 0x0b 
+         "CmdRef",         // 0x0c 
+         "Copy",           // 0x0d 
+         "Cred",           // 0x0e 
+         "Data",           // 0x0f 
+         "Delete",         // 0x10 
+         "Exec",           // 0x11 
+         "Final",          // 0x12 
+         "Get",            // 0x13 
+         "Item",           // 0x14 
+         "Lang",           // 0x15 
+         "LocName",        // 0x16 
+         "LocURI",         // 0x17 
+         "Map",            // 0x18 
+         "MapItem",        // 0x19 
+         "Meta",           // 0x1a 
+         "MsgID",          // 0x1b 
+         "MsgRef",         // 0x1c 
+         "NoResp",         // 0x1d 
+         "NoResults",      // 0x1e 
+         "Put",            // 0x1f 
+         "Replace",        // 0x20 
+         "RespURI",        // 0x21 
+         "Results",        // 0x22 
+         "Search",         // 0x23 
+         "Sequence",       // 0x24 
+         "SessionID",      // 0x25 
+         "SftDel",         // 0x26 
+         "Source",         // 0x27 
+         "SourceRef",      // 0x28 
+         "Status",         // 0x29 
+         "Sync",           // 0x2a 
+         "SyncBody",       // 0x2b 
+         "SyncHdr",        // 0x2c 
+         "SyncML",         // 0x2d 
+         "Target",         // 0x2e 
+         "TargetRef",      // 0x2f 
+         "Reserved for future use",    // 0x30 
+         "VerDTD",         // 0x31 
+         "VerProto",       // 0x32 
+         "NumberOfChanged",// 0x33 
+         "MoreData",       // 0x34 
+         "Field",          // 0x35
+         "Filter",         // 0x36
+         "Record",         // 0x37
+         "FilterType",     // 0x38
+         "SourceParent",   // 0x39
+         "TargetParent",   // 0x3a
+         "Move",           // 0x3b
+         "Correlator"      // 0x3c
+    };  
+    
+    public static final String [] TAG_TABLE_1 = {
+       
+         //  -//SYNCML//DTD MetInf 1.2//EN 
+        
+         "Anchor",         // 0x05 
+         "EMI",            // 0x06 
+         "Format",         // 0x07 
+         "FreeID",         // 0x08 
+         "FreeMem",        // 0x09 
+         "Last",           // 0x0a 
+         "Mark",           // 0x0b 
+         "MaxMsgSize",     // 0x0c 
+         "Mem",            // 0x0d 
+         "MetInf",         // 0x0e 
+         "Next",           // 0x0f 
+         "NextNonce",      // 0x10 
+         "SharedMem",      // 0x11 
+         "Size",           // 0x12 
+         "Type",           // 0x13 
+         "Version",        // 0x14 
+         "MaxObjSize",     // 0x15
+         "FieldLevel"      // 0x16
+         
+    };
+
+    public static final String [] TAG_TABLE_2_DM = {
+        
+        //  -//OMA//DTD-DM-DDF 1.2//EN 
+       
+        "AccessType",         // 0x05 
+        "ACL",                // 0x06 
+        "Add",                // 0x07 
+        "b64",                // 0x08 
+        "bin",                // 0x09 
+        "bool",               // 0x0a 
+        "chr",                // 0x0b 
+        "CaseSense",          // 0x0c 
+        "CIS",                // 0x0d 
+        "Copy",               // 0x0e 
+        "CS",                 // 0x0f 
+        "date",               // 0x10 
+        "DDFName",            // 0x11 
+        "DefaultValue",       // 0x12 
+        "Delete",             // 0x13 
+        "Description",        // 0x14 
+        "DDFFormat",          // 0x15 
+        "DFProperties",       // 0x16 
+        "DFTitle",            // 0x17 
+        "DFType",             // 0x18 
+        "Dynamic",            // 0x19 
+        "Exec",               // 0x1a 
+        "float",              // 0x1b 
+        "Format",             // 0x1c 
+        "Get",                // 0x1d 
+        "int",                // 0x1e 
+        "Man",                // 0x1f 
+        "MgmtTree",           // 0x20 
+        "MIME",               // 0x21 
+        "Mod",                // 0x22 
+        "Name",               // 0x23 
+        "Node",               // 0x24 
+        "node",               // 0x25 
+        "NodeName",           // 0x26 
+        "null",               // 0x27 
+        "Occurence",          // 0x28 
+        "One",                // 0x29 
+        "OneOrMore",          // 0x2a 
+        "OneOrN",             // 0x2b 
+        "Path",               // 0x2c 
+        "Permanent",          // 0x2d 
+        "Replace",            // 0x2e 
+        "RTProperties",       // 0x2f 
+        "Scope",              // 0x30 
+        "Size",               // 0x31 
+        "time",               // 0x32 
+        "Title",              // 0x33 
+        "TStamp",             // 0x34 
+        "Type",               // 0x35
+        "Value",              // 0x36
+        "VerDTD",             // 0x37
+        "VerNo",              // 0x38
+        "xml",                // 0x39
+        "ZeroOrMore",         // 0x3a
+        "ZeroOrN",            // 0x3b
+        "ZeroOrOne"           // 0x3c
+        
+   };
+    
+}
+
diff --git a/xml/src/main/java/org/kxml2/wap/wml/Wml.java b/xml/src/main/java/org/kxml2/wap/wml/Wml.java
new file mode 100644
index 0000000..7e925d8
--- /dev/null
+++ b/xml/src/main/java/org/kxml2/wap/wml/Wml.java
@@ -0,0 +1,233 @@
+package org.kxml2.wap.wml;
+
+import org.kxml2.wap.*;
+
+
+/** This class contains the wml coding tables for elements 
+ *  and attributes needed by the WmlParser. 
+ */
+
+
+public abstract class Wml {
+
+    /** Creates a WbxmlParser with the WML code pages set */
+
+    public static WbxmlParser createParser() {
+        WbxmlParser p = new WbxmlParser();
+        p.setTagTable(0, TAG_TABLE);
+        p.setAttrStartTable(0, ATTR_START_TABLE);
+        p.setAttrValueTable(0, ATTR_VALUE_TABLE);
+        return p;
+    }
+
+    public static WbxmlSerializer createSerializer() {
+        WbxmlSerializer s = new WbxmlSerializer();
+        s.setTagTable(0, TAG_TABLE);
+        s.setAttrStartTable(0, ATTR_START_TABLE);
+        s.setAttrValueTable(0, ATTR_VALUE_TABLE);
+        return s;
+    }
+
+
+    public static final String [] TAG_TABLE = {
+
+    null, // 05
+    null, // 06
+    null, // 07
+    null, // 08
+    null, // 09
+    null, // 0A
+    null, // 0B
+    null, // 0C
+    null, // 0D
+    null, // 0E
+    null, // 0F
+
+    null, // 10
+    null, // 11
+    null, // 12
+    null, // 13
+    null, // 14
+    null, // 15
+    null, // 16
+    null, // 17
+    null, // 18
+    null, // 19
+    null, // 1A
+    null, // 1B
+    "a",  // 1C
+    "td", // 1D
+    "tr", // 1E
+    "table", // 1F
+
+    "p", // 20
+    "postfield", // 21
+    "anchor", // 22
+    "access", // 23
+    "b",  // 24
+    "big", // 25
+    "br", // 26
+    "card", // 27
+    "do", // 28
+    "em", // 29
+    "fieldset", // 2A
+    "go", // 2B
+    "head", // 2C
+    "i", // 2D
+    "img", // 2E
+    "input", // 2F
+
+    "meta", // 30
+    "noop", // 31
+    "prev", // 32
+    "onevent", // 33
+    "optgroup", // 34
+    "option", // 35
+    "refresh", // 36
+    "select", // 37
+    "small", // 38
+    "strong", // 39
+    null, // 3A
+    "template", // 3B
+    "timer", // 3C
+    "u", // 3D
+    "setvar", // 3E
+    "wml", // 3F
+    };
+
+    
+    public static final String [] ATTR_START_TABLE = { 
+    "accept-charset", // 05
+    "align=bottom", // 06
+    "align=center", // 07
+    "align=left", // 08
+    "align=middle", // 09
+    "align=right", // 0A
+    "align=top", // 0B
+    "alt", // 0C
+    "content", // 0D
+    null, // 0E
+    "domain", // 0F
+    
+    "emptyok=false", // 10
+    "emptyok=true", // 11
+    "format", // 12
+    "height", // 13
+    "hspace", // 14
+    "ivalue", // 15
+    "iname", // 16
+    null, // 17
+    "label", // 18
+    "localsrc", // 19
+    "maxlength", // 1A
+    "method=get", // 1B
+    "method=post", // 1C
+    "mode=nowrap", // 1D
+    "mode=wrap", // 1E
+    "multiple=false", // 1F
+
+    "multiple=true", // 20
+    "name", // 21
+    "newcontext=false", // 22
+    "newcontext=true", // 23
+    "onpick", // 24
+    "onenterbackward", // 25
+    "onenterforward", // 26
+    "ontimer", // 27
+    "optimal=false", // 28
+    "optimal=true", // 29
+    "path", // 2A
+    null, // 2B
+    null, // 2C
+    null, // 2D
+    "scheme", // 2E
+    "sendreferer=false", // 2F
+    
+    "sendreferer=true", // 30
+    "size", // 31
+    "src", // 32
+    "ordered=true", // 33
+    "ordered=false", // 34
+    "tabindex", // 35
+    "title", // 36
+    "type", // 37
+    "type=accept", // 38
+    "type=delete", // 39
+    "type=help", // 3A
+    "type=password", // 3B
+    "type=onpick", // 3C
+    "type=onenterbackward", // 3D
+    "type=onenterforward", // 3E
+    "type=ontimer", // 3F
+
+    null, // 40
+    null, // 41
+    null, // 42
+    null, // 43
+    null, // 44
+    "type=options", // 45
+    "type=prev", // 46
+    "type=reset", // 47
+    "type=text", // 48
+    "type=vnd.", // 49
+    "href", // 4A
+    "href=http://", // 4B
+    "href=https://", // 4C
+    "value", // 4D
+    "vspace", // 4E
+    "width", // 4F
+
+    "xml:lang", // 50
+    null, // 51
+    "align", // 52
+    "columns", // 53
+    "class", // 54
+    "id", // 55
+    "forua=false", // 56
+    "forua=true", // 57
+    "src=http://", // 58
+    "src=https://", // 59
+    "http-equiv", // 5A
+    "http-equiv=Content-Type", // 5B
+    "content=application/vnd.wap.wmlc;charset=", // 5C
+    "http-equiv=Expires", // 5D
+    null, // 5E
+    null, // 5F
+    };
+
+
+    public static final String [] ATTR_VALUE_TABLE = {
+    ".com/", // 85
+    ".edu/", // 86
+    ".net/", // 87
+    ".org/", // 88
+    "accept", // 89
+    "bottom", // 8A
+    "clear", // 8B
+    "delete", // 8C
+    "help", // 8D
+    "http://", // 8E
+    "http://www.", // 8F
+    
+    "https://", // 90
+    "https://www.", // 91
+    null, // 92
+    "middle", // 93
+    "nowrap", // 94
+    "onpick", // 95
+    "onenterbackward", // 96
+    "onenterforward", // 97
+    "ontimer", // 98
+    "options", // 99
+    "password", // 9A
+    "reset", // 9B
+    null, // 9C
+    "text", // 9D
+    "top", // 9E
+    "unknown", // 9F
+    
+    "wrap", // A0
+    "www.", // A1
+    };
+}    
+
diff --git a/xml/src/main/java/org/kxml2/wap/wv/WV.java b/xml/src/main/java/org/kxml2/wap/wv/WV.java
new file mode 100644
index 0000000..e2afbfb
--- /dev/null
+++ b/xml/src/main/java/org/kxml2/wap/wv/WV.java
@@ -0,0 +1,593 @@
+package org.kxml2.wap.wv;
+
+import java.io.IOException;
+
+import org.kxml2.wap.*;
+
+/*
+
+ * WV.java
+
+ *
+
+ * Created on 25 September 2003, 10:40
+
+ */
+
+
+
+
+
+   /** 
+     *    Wireless Village CSP 1.1 ("OMA-WV-CSP-V1_1-20021001-A.pdf")
+     *    Wireless Village CSP 1.2 ("OMA-IMPS-WV-CSP_WBXML-v1_2-20030221-C.PDF")
+     *    There are some bugs in the 1.2 spec but this is Ok. 1.2 is candidate  
+ *
+
+ * @author  Bogdan Onoiu
+
+ */
+
+public abstract class WV {
+
+    
+
+    
+    
+    public static WbxmlParser createParser () throws IOException {
+        
+        WbxmlParser parser = new WbxmlParser();
+
+        parser.setTagTable (0, WV.tagTablePage0);
+        parser.setTagTable (1, WV.tagTablePage1);
+        parser.setTagTable (2, WV.tagTablePage2);
+        parser.setTagTable (3, WV.tagTablePage3);
+        parser.setTagTable (4, WV.tagTablePage4);
+        parser.setTagTable (5, WV.tagTablePage5);
+        parser.setTagTable (6, WV.tagTablePage6);
+        parser.setTagTable (7, WV.tagTablePage7);
+        parser.setTagTable (8, WV.tagTablePage8);
+        parser.setTagTable (9, WV.tagTablePage9);
+        parser.setTagTable (10, WV.tagTablePageA);
+
+        parser.setAttrStartTable (0, WV.attrStartTable);
+        
+        parser.setAttrValueTable (0, WV.attrValueTable);
+
+        return parser;
+    }
+    
+   
+    
+    public static final String [] tagTablePage0 = {
+        /* Common ... continue on Page 0x09 */
+        "Acceptance",     //0x00, 0x05
+        "AddList",        //0x00, 0x06
+        "AddNickList",    //0x00, 0x07
+        "SName",          //0x00, 0x08
+        "WV-CSP-Message", //0x00, 0x09
+        "ClientID",       //0x00, 0x0A
+        "Code",           //0x00, 0x0B
+        "ContactList",    //0x00, 0x0C
+        "ContentData",    //0x00, 0x0D
+        "ContentEncoding",//0x00, 0x0E
+        "ContentSize",    //0x00, 0x0F
+        "ContentType",    //0x00, 0x10
+        "DateTime",       //0x00, 0x11
+        "Description",    //0x00, 0x12
+        "DetailedResult", //0x00, 0x13
+        "EntityList",     //0x00, 0x14
+        "Group",          //0x00, 0x15
+        "GroupID",        //0x00, 0x16
+        "GroupList",      //0x00, 0x17
+        "InUse",          //0x00, 0x18
+        "Logo",           //0x00, 0x19
+        "MessageCount",   //0x00, 0x1A
+        "MessageID",      //0x00, 0x1B
+        "MessageURI",     //0x00, 0x1C
+        "MSISDN",         //0x00, 0x1D
+        "Name",           //0x00, 0x1E
+        "NickList",       //0x00, 0x1F
+        "NickName",       //0x00, 0x20
+        "Poll",           //0x00, 0x21
+        "Presence",       //0x00, 0x22
+        "PresenceSubList",//0x00, 0x23
+        "PresenceValue",  //0x00, 0x24
+        "Property",       //0x00, 0x25
+        "Qualifier",      //0x00, 0x26
+        "Recipient",      //0x00, 0x27
+        "RemoveList",     //0x00, 0x28
+        "RemoveNickList", //0x00, 0x29
+        "Result",         //0x00, 0x2A
+        "ScreenName",     //0x00, 0x2B
+        "Sender",         //0x00, 0x2C
+        "Session",        //0x00, 0x2D
+        "SessionDescriptor",//0x00, 0x2E
+        "SessionID",      //0x00, 0x2F
+        "SessionType",    //0x00, 0x30
+        "Status",         //0x00, 0x31
+        "Transaction",    //0x00, 0x32
+        "TransactionContent",//0x00, 0x33
+        "TransactionDescriptor",//0x00, 0x34
+        "TransactionID",  //0x00, 0x35
+        "TransactionMode",//0x00, 0x36
+        "URL",            //0x00, 0x37
+        "URLList",        //0x00, 0x38
+        "User",           //0x00, 0x39
+        "UserID",         //0x00, 0x3A
+        "UserList",       //0x00, 0x3B
+        "Validity",       //0x00, 0x3C
+        "Value",          //0x00, 0x3D
+    };
+    
+    public static final String [] tagTablePage1 = {
+        /* Access ... continue on Page 0x0A */
+        "AllFunctions",             //  0x01, 0x05
+        "AllFunctionsRequest",      //  0x01, 0x06
+        "CancelInvite-Request",     //  0x01, 0x07
+        "CancelInviteUser-Request", //  0x01, 0x08
+        "Capability",               //  0x01, 0x09
+        "CapabilityList",           //  0x01, 0x0A
+        "CapabilityRequest",        //  0x01, 0x0B
+        "ClientCapability-Request", //  0x01, 0x0C
+        "ClientCapability-Response",//  0x01, 0x0D
+        "DigestBytes",          //  0x01, 0x0E
+        "DigestSchema",         //  0x01, 0x0F
+        "Disconnect",           //  0x01, 0x10
+        "Functions",            //  0x01, 0x11
+        "GetSPInfo-Request",    //  0x01, 0x12
+        "GetSPInfo-Response",   //  0x01, 0x13
+        "InviteID",             //  0x01, 0x14
+        "InviteNote",           //  0x01, 0x15
+        "Invite-Request",       //  0x01, 0x16
+        "Invite-Response",      //  0x01, 0x17
+        "InviteType",           //  0x01, 0x18
+        "InviteUser-Request",   //  0x01, 0x19
+        "InviteUser-Response",  //  0x01, 0x1A
+        "KeepAlive-Request",    //  0x01, 0x1B
+        "KeepAliveTime",        //  0x01, 0x1C
+        "Login-Request",        //  0x01, 0x1D
+        "Login-Response",       //  0x01, 0x1E
+        "Logout-Request",       //  0x01, 0x1F
+        "Nonce",                //  0x01, 0x20
+        "Password",             //  0x01, 0x21
+        "Polling-Request",      //  0x01, 0x22
+        "ResponseNote",         //  0x01, 0x23
+        "SearchElement",        //  0x01, 0x24
+        "SearchFindings",       //  0x01, 0x25
+        "SearchID",             //  0x01, 0x26
+        "SearchIndex",          //  0x01, 0x27
+        "SearchLimit",          //  0x01, 0x28
+        "KeepAlive-Response",   //  0x01, 0x29
+        "SearchPairList",       //  0x01, 0x2A
+        "Search-Request",       //  0x01, 0x2B
+        "Search-Response",      //  0x01, 0x2C
+        "SearchResult",         //  0x01, 0x2D
+        "Service-Request",      //  0x01, 0x2E
+        "Service-Response",     //  0x01, 0x2F
+        "SessionCookie",        //  0x01, 0x30
+        "StopSearch-Request",   //  0x01, 0x31
+        "TimeToLive",           //  0x01, 0x32
+        "SearchString",         //  0x01, 0x33
+        "CompletionFlag",       //  0x01, 0x34
+        null,                   //  0x01, 0x35
+        "ReceiveList",          //  0x01, 0x36 /* WV 1.2 */
+        "VerifyID-Request",     //  0x01, 0x37 /* WV 1.2 */
+        "Extended-Request",     //  0x01, 0x38 /* WV 1.2 */
+        "Extended-Response",    //  0x01, 0x39 /* WV 1.2 */
+        "AgreedCapabilityList", //  0x01, 0x3A /* WV 1.2 */
+        "Extended-Data",        //  0x01, 0x3B /* WV 1.2 */
+        "OtherServer",          //  0x01, 0x3C /* WV 1.2 */
+        "PresenceAttributeNSName",//0x01, 0x3D /* WV 1.2 */
+        "SessionNSName",        //  0x01, 0x3E /* WV 1.2 */
+        "TransactionNSName",    //  0x01, 0x3F /* WV 1.2 */
+    };
+    
+    public static final String [] tagTablePage2 = {
+        /* Service ... continue on Page 0x08 */
+        "ADDGM",        //  0x02, 0x05
+        "AttListFunc",  //  0x02, 0x06
+        "BLENT",        //  0x02, 0x07
+        "CAAUT",        //  0x02, 0x08
+        "CAINV",        //  0x02, 0x09
+        "CALI",         //  0x02, 0x0A
+        "CCLI",         //  0x02, 0x0B
+        "ContListFunc", //  0x02, 0x0C
+        "CREAG",        //  0x02, 0x0D
+        "DALI",         //  0x02, 0x0E
+        "DCLI",         //  0x02, 0x0F
+        "DELGR",        //  0x02, 0x10
+        "FundamentalFeat",//0x02, 0x11
+        "FWMSG",        //  0x02, 0x12
+        "GALS",         //  0x02, 0x13
+        "GCLI",         //  0x02, 0x14
+        "GETGM",        //  0x02, 0x15
+        "GETGP",        //  0x02, 0x16
+        "GETLM",        //  0x02, 0x17
+        "GETM",         //  0x02, 0x18
+        "GETPR",        //  0x02, 0x19
+        "GETSPI",       //  0x02, 0x1A
+        "GETWL",        //  0x02, 0x1B
+        "GLBLU",        //  0x02, 0x1C
+        "GRCHN",        //  0x02, 0x1D
+        "GroupAuthFunc",//  0x02, 0x1E
+        "GroupFeat",    //  0x02, 0x1F
+        "GroupMgmtFunc",//  0x02, 0x20
+        "GroupUseFunc", //  0x02, 0x21
+        "IMAuthFunc",   //  0x02, 0x22
+        "IMFeat",       //  0x02, 0x23
+        "IMReceiveFunc",//  0x02, 0x24
+        "IMSendFunc",   //  0x02, 0x25
+        "INVIT",        //  0x02, 0x26
+        "InviteFunc",   //  0x02, 0x27
+        "MBRAC",        //  0x02, 0x28
+        "MCLS",         //  0x02, 0x29
+        "MDELIV",       //  0x02, 0x2A
+        "NEWM",         //  0x02, 0x2B
+        "NOTIF",        //  0x02, 0x2C
+        "PresenceAuthFunc",//0x02, 0x2D
+        "PresenceDeliverFunc",//0x02, 0x2E
+        "PresenceFeat", //  0x02, 0x2F
+        "REACT",        //  0x02, 0x30
+        "REJCM",        //  0x02, 0x31
+        "REJEC",        //  0x02, 0x32
+        "RMVGM",        //  0x02, 0x33
+        "SearchFunc",   //  0x02, 0x34
+        "ServiceFunc",  //  0x02, 0x35
+        "SETD",         //  0x02, 0x36
+        "SETGP",        //  0x02, 0x37
+        "SRCH",         //  0x02, 0x38
+        "STSRC",        //  0x02, 0x39
+        "SUBGCN",       //  0x02, 0x3A
+        "UPDPR",        //  0x02, 0x3B
+        "WVCSPFeat",    //  0x02, 0x3C
+        "MF",           //  0x02, 0x3D /* WV 1.2 */
+        "MG",           //  0x02, 0x3E /* WV 1.2 */
+        "MM"            //  0x02, 0x3F /* WV 1.2 */
+    };
+    
+    public static final String [] tagTablePage3 = {
+        /* Client Capability */
+        "AcceptedCharset",          //  0x03, 0x05
+        "AcceptedContentLength",    //  0x03, 0x06
+        "AcceptedContentType",      //  0x03, 0x07
+        "AcceptedTransferEncoding", //  0x03, 0x08
+        "AnyContent",               //  0x03, 0x09
+        "DefaultLanguage",          //  0x03, 0x0A
+        "InitialDeliveryMethod",    //  0x03, 0x0B
+        "MultiTrans",               //  0x03, 0x0C
+        "ParserSize",               //  0x03, 0x0D
+        "ServerPollMin",            //  0x03, 0x0E
+        "SupportedBearer",          //  0x03, 0x0F
+        "SupportedCIRMethod",       //  0x03, 0x10
+        "TCPAddress",               //  0x03, 0x11
+        "TCPPort",                  //  0x03, 0x12
+        "UDPPort"                  //  0x03, 0x13
+    };
+    
+    public static final String [] tagTablePage4 = {
+        /* Presence Primitive */
+        "CancelAuth-Request",           //  0x04, 0x05
+        "ContactListProperties",        //  0x04, 0x06
+        "CreateAttributeList-Request",  //  0x04, 0x07
+        "CreateList-Request",           //  0x04, 0x08
+        "DefaultAttributeList",         //  0x04, 0x09
+        "DefaultContactList",           //  0x04, 0x0A
+        "DefaultList",                  //  0x04, 0x0B
+        "DeleteAttributeList-Request",  //  0x04, 0x0C
+        "DeleteList-Request",           //  0x04, 0x0D
+        "GetAttributeList-Request",     //  0x04, 0x0E
+        "GetAttributeList-Response",    //  0x04, 0x0F
+        "GetList-Request",              //  0x04, 0x10
+        "GetList-Response",             //  0x04, 0x11
+        "GetPresence-Request",          //  0x04, 0x12
+        "GetPresence-Response",         //  0x04, 0x13
+        "GetWatcherList-Request",       //  0x04, 0x14
+        "GetWatcherList-Response",      //  0x04, 0x15
+        "ListManage-Request",           //  0x04, 0x16
+        "ListManage-Response",          //  0x04, 0x17
+        "UnsubscribePresence-Request",  //  0x04, 0x18
+        "PresenceAuth-Request",         //  0x04, 0x19
+        "PresenceAuth-User",            //  0x04, 0x1A
+        "PresenceNotification-Request", //  0x04, 0x1B
+        "UpdatePresence-Request",       //  0x04, 0x1C
+        "SubscribePresence-Request",    //  0x04, 0x1D
+        "Auto-Subscribe",               //  0x04, 0x1E /* WV 1.2 */
+        "GetReactiveAuthStatus-Request",//  0x04, 0x1F /* WV 1.2 */
+        "GetReactiveAuthStatus-Response",// 0x04, 0x20 /* WV 1.2 */
+    };
+    
+    public static final String [] tagTablePage5 = {
+        /* Presence Attribute */
+        "Accuracy",         //  0x05, 0x05
+        "Address",          //  0x05, 0x06
+        "AddrPref",         //  0x05, 0x07
+        "Alias",            //  0x05, 0x08
+        "Altitude",         //  0x05, 0x09
+        "Building",         //  0x05, 0x0A
+        "Caddr",            //  0x05, 0x0B
+        "City",             //  0x05, 0x0C
+        "ClientInfo",       //  0x05, 0x0D
+        "ClientProducer",   //  0x05, 0x0E
+        "ClientType",       //  0x05, 0x0F
+        "ClientVersion",    //  0x05, 0x10
+        "CommC",            //  0x05, 0x11
+        "CommCap",          //  0x05, 0x12
+        "ContactInfo",      //  0x05, 0x13
+        "ContainedvCard",   //  0x05, 0x14
+        "Country",          //  0x05, 0x15
+        "Crossing1",        //  0x05, 0x16
+        "Crossing2",        //  0x05, 0x17
+        "DevManufacturer",  //  0x05, 0x18
+        "DirectContent",    //  0x05, 0x19
+        "FreeTextLocation", //  0x05, 0x1A
+        "GeoLocation",      //  0x05, 0x1B
+        "Language",         //  0x05, 0x1C
+        "Latitude",         //  0x05, 0x1D
+        "Longitude",        //  0x05, 0x1E
+        "Model",            //  0x05, 0x1F
+        "NamedArea",        //  0x05, 0x20
+        "OnlineStatus",     //  0x05, 0x21
+        "PLMN",             //  0x05, 0x22
+        "PrefC",            //  0x05, 0x23
+        "PreferredContacts",//  0x05, 0x24
+        "PreferredLanguage",//  0x05, 0x25
+        "PreferredContent", //  0x05, 0x26
+        "PreferredvCard",   //  0x05, 0x27
+        "Registration",     //  0x05, 0x28
+        "StatusContent",    //  0x05, 0x29
+        "StatusMood",       //  0x05, 0x2A
+        "StatusText",       //  0x05, 0x2B
+        "Street",           //  0x05, 0x2C
+        "TimeZone",         //  0x05, 0x2D
+        "UserAvailability", //  0x05, 0x2E
+        "Cap",              //  0x05, 0x2F
+        "Cname",            //  0x05, 0x30
+        "Contact",          //  0x05, 0x31
+        "Cpriority",        //  0x05, 0x32
+        "Cstatus",          //  0x05, 0x33
+        "Note",             //  0x05, 0x34 /* WV 1.2 */
+        "Zone",             //  0x05, 0x35
+        null,
+        "Inf_link",         //  0x05, 0x37 /* WV 1.2 */
+        "InfoLink",         //  0x05, 0x38 /* WV 1.2 */
+        "Link",             //  0x05, 0x39 /* WV 1.2 */
+        "Text",             //  0x05, 0x3A /* WV 1.2 */
+    };
+    
+    public static final String [] tagTablePage6 = {
+        /* Messaging */
+        "BlockList",                //  0x06, 0x05
+//      "BlockUser-Request",        //  0x06, 0x06  //This is a bug in the spec
+        "BlockEntity-Request",        //  0x06, 0x06  
+        "DeliveryMethod",           //  0x06, 0x07
+        "DeliveryReport",           //  0x06, 0x08
+        "DeliveryReport-Request",   //  0x06, 0x09
+        "ForwardMessage-Request",   //  0x06, 0x0A
+        "GetBlockedList-Request",   //  0x06, 0x0B
+        "GetBlockedList-Response",  //  0x06, 0x0C
+        "GetMessageList-Request",   //  0x06, 0x0D
+        "GetMessageList-Response",  //  0x06, 0x0E
+        "GetMessage-Request",       //  0x06, 0x0F
+        "GetMessage-Response",      //  0x06, 0x10
+        "GrantList",                //  0x06, 0x11
+        "MessageDelivered",         //  0x06, 0x12
+        "MessageInfo",              //  0x06, 0x13
+        "MessageNotification",      //  0x06, 0x14
+        "NewMessage",               //  0x06, 0x15
+        "RejectMessage-Request",    //  0x06, 0x16
+        "SendMessage-Request",      //  0x06, 0x17
+        "SendMessage-Response",     //  0x06, 0x18
+        "SetDeliveryMethod-Request",//  0x06, 0x19
+        "DeliveryTime",             //  0x06, 0x1A
+    };
+    
+    public static final String [] tagTablePage7 = {
+        /* Group */
+        "AddGroupMembers-Request",  //  0x07, 0x05
+        "Admin",                    //  0x07, 0x06
+        "CreateGroup-Request",      //  0x07, 0x07
+        "DeleteGroup-Request",      //  0x07, 0x08
+        "GetGroupMembers-Request",  //  0x07, 0x09
+        "GetGroupMembers-Response", //  0x07, 0x0A
+        "GetGroupProps-Request",    //  0x07, 0x0B
+        "GetGroupProps-Response",   //  0x07, 0x0C
+        "GroupChangeNotice",        //  0x07, 0x0D
+        "GroupProperties",          //  0x07, 0x0E
+        "Joined",                   //  0x07, 0x0F
+        "JoinedRequest",            //  0x07, 0x10
+        "JoinGroup-Request",        //  0x07, 0x11
+        "JoinGroup-Response",       //  0x07, 0x12
+        "LeaveGroup-Request",       //  0x07, 0x13
+        "LeaveGroup-Response",      //  0x07, 0x14
+        "Left",                     //  0x07, 0x15
+        "MemberAccess-Request",     //  0x07, 0x16
+        "Mod",                      //  0x07, 0x17
+        "OwnProperties",            //  0x07, 0x18
+        "RejectList-Request",       //  0x07, 0x19
+        "RejectList-Response",      //  0x07, 0x1A
+        "RemoveGroupMembers-Request",// 0x07, 0x1B
+        "SetGroupProps-Request",    //  0x07, 0x1C
+        "SubscribeGroupNotice-Request", //  0x07, 0x1D
+        "SubscribeGroupNotice-Response",//  0x07, 0x1E
+        "Users",                    //  0x07, 0x1F
+        "WelcomeNote",              //  0x07, 0x20
+        "JoinGroup",                //  0x07, 0x21
+        "SubscribeNotification",    //  0x07, 0x22
+        "SubscribeType",            //  0x07, 0x23
+        "GetJoinedUsers-Request",   //  0x07, 0x24 /* WV 1.2 */
+        "GetJoinedUsers-Response",  //  0x07, 0x25 /* WV 1.2 */
+        "AdminMapList",             //  0x07, 0x26 /* WV 1.2 */
+        "AdminMapping",             //  0x07, 0x27 /* WV 1.2 */
+        "Mapping",                  //  0x07, 0x28 /* WV 1.2 */
+        "ModMapping",               //  0x07, 0x29 /* WV 1.2 */
+        "UserMapList",              //  0x07, 0x2A /* WV 1.2 */
+        "UserMapping",              //  0x07, 0x2B /* WV 1.2 */
+    };
+    
+    public static final String [] tagTablePage8 = {
+        /* Service ... continued */
+        "MP",                       //  0x08, 0x05 /* WV 1.2 */
+        "GETAUT",                   //  0x08, 0x06 /* WV 1.2 */
+        "GETJU",                    //  0x08, 0x07 /* WV 1.2 */
+        "VRID",                     //  0x08, 0x08 /* WV 1.2 */
+        "VerifyIDFunc",             //  0x08, 0x09 /* WV 1.2 */
+    };
+    
+    public static final String [] tagTablePage9 = {
+        /* Common ... continued */
+        "CIR",                      //  0x09, 0x05 /* WV 1.2 */
+        "Domain",                   //  0x09, 0x06 /* WV 1.2 */
+        "ExtBlock",                 //  0x09, 0x07 /* WV 1.2 */
+        "HistoryPeriod",            //  0x09, 0x08 /* WV 1.2 */
+        "IDList",                   //  0x09, 0x09 /* WV 1.2 */
+        "MaxWatcherList",           //  0x09, 0x0A /* WV 1.2 */
+        "ReactiveAuthState",        //  0x09, 0x0B /* WV 1.2 */
+        "ReactiveAuthStatus",       //  0x09, 0x0C /* WV 1.2 */
+        "ReactiveAuthStatusList",   //  0x09, 0x0D /* WV 1.2 */
+        "Watcher",                  //  0x09, 0x0E /* WV 1.2 */
+        "WatcherStatus"             //  0x09, 0x0F /* WV 1.2 */
+    };
+    
+    public static final String [] tagTablePageA = {
+        /* Access ... continued */
+        "WV-CSP-NSDiscovery-Request",  //0x0A, 0x05 /* WV 1.2 */
+        "WV-CSP-NSDiscovery-Response", //0x0A, 0x06 /* WV 1.2 */
+        "VersionList"                  //0x0A, 0x07 /* WV 1.2 */
+    };
+    
+    public static final String [] attrStartTable = {
+        "xmlns=http://www.wireless-village.org/CSP",//  0x00, 0x05
+        "xmlns=http://www.wireless-village.org/PA", //  0x00, 0x06
+        "xmlns=http://www.wireless-village.org/TRC",//  0x00, 0x07
+        "xmlns=http://www.openmobilealliance.org/DTD/WV-CSP",   //  0x00, 0x08
+        "xmlns=http://www.openmobilealliance.org/DTD/WV-PA",    //  0x00, 0x09
+        "xmlns=http://www.openmobilealliance.org/DTD/WV-TRC",   //  0x00, 0x0A
+    };
+    
+    public static final String [] attrValueTable = {
+      
+        "AccessType",                           // 0x00 /* Common value token */
+        "ActiveUsers",                          // 0x01 /* Common value token */
+        "Admin",                                // 0x02 /* Common value token */
+        "application/",                         // 0x03 /* Common value token */
+        "application/vnd.wap.mms-message",      // 0x04 /* Common value token */
+        "application/x-sms",                    // 0x05 /* Common value token */
+        "AutoJoin",                             // 0x06 /* Common value token */
+        "BASE64",                               // 0x07 /* Common value token */
+        "Closed",                               // 0x08 /* Common value token */
+        "Default",                              // 0x09 /* Common value token */
+        "DisplayName",                          // 0x0a /* Common value token */
+        "F",                                    // 0x0b /* Common value token */
+        "G",                                    // 0x0c /* Common value token */
+        "GR",                                   // 0x0d /* Common value token */
+        "http://",                              // 0x0e /* Common value token */
+        "https://",                             // 0x0f /* Common value token */
+        "image/",                               // 0x10 /* Common value token */
+        "Inband",                               // 0x11 /* Common value token */
+        "IM",                                   // 0x12 /* Common value token */
+        "MaxActiveUsers",                       // 0x13 /* Common value token */
+        "Mod",                                  // 0x14 /* Common value token */
+        "Name",                                 // 0x15 /* Common value token */
+        "None",                                 // 0x16 /* Common value token */
+        "N",                                    // 0x17 /* Common value token */
+        "Open",                                 // 0x18 /* Common value token */
+        "Outband",                              // 0x19 /* Common value token */
+        "PR",                                   // 0x1a /* Common value token */
+        "Private",                              // 0x1b /* Common value token */
+        "PrivateMessaging",                     // 0x1c /* Common value token */
+        "PrivilegeLevel",                       // 0x1d /* Common value token */
+        "Public",                               // 0x1e /* Common value token */
+        "P",                                    // 0x1f /* Common value token */
+        "Request",                              // 0x20 /* Common value token */
+        "Response",                             // 0x21 /* Common value token */
+        "Restricted",                           // 0x22 /* Common value token */
+        "ScreenName",                           // 0x23 /* Common value token */
+        "Searchable",                           // 0x24 /* Common value token */
+        "S",                                    // 0x25 /* Common value token */
+        "SC",                                   // 0x26 /* Common value token */
+        "text/",                                // 0x27 /* Common value token */
+        "text/plain",                           // 0x28 /* Common value token */
+        "text/x-vCalendar",                     // 0x29 /* Common value token */
+        "text/x-vCard",                         // 0x2a /* Common value token */
+        "Topic",                                // 0x2b /* Common value token */
+        "T",                                    // 0x2c /* Common value token */
+        "Type",                                 // 0x2d /* Common value token */
+        "U",                                    // 0x2e /* Common value token */
+        "US",                                   // 0x2f /* Common value token */
+        "www.wireless-village.org",             // 0x30 /* Common value token */
+        "AutoDelete",                           // 0x31 /* Common value token */ /* WV 1.2 */
+        "GM",                                   // 0x32 /* Common value token */ /* WV 1.2 */
+        "Validity",                             // 0x33 /* Common value token */ /* WV 1.2 */
+        "ShowID",                               // 0x34 /* Common value token */ /* WV 1.2 */
+        "GRANTED",                              // 0x35 /* Common value token */ /* WV 1.2 */
+        "PENDING",                              // 0x36 /* Common value token */ /* WV 1.2 */
+        null,                                   // 0x37
+        null,                                   // 0x38
+        null,                                   // 0x39
+        null,                                   // 0x3a
+        null,                                   // 0x3b
+        null,                                   // 0x3c
+        "GROUP_ID",                             // 0x3d /* Access value token */
+        "GROUP_NAME",                           // 0x3e /* Access value token */
+        "GROUP_TOPIC",                          // 0x3f /* Access value token */
+        "GROUP_USER_ID_JOINED",                 // 0x40 /* Access value token */
+        "GROUP_USER_ID_OWNER",                  // 0x41 /* Access value token */
+        "HTTP",                                 // 0x42 /* Access value token */
+        "SMS",                                  // 0x43 /* Access value token */
+        "STCP",                                 // 0x44 /* Access value token */
+        "SUDP",                                 // 0x45 /* Access value token */
+        "USER_ALIAS",                           // 0x46 /* Access value token */
+        "USER_EMAIL_ADDRESS",                   // 0x47 /* Access value token */
+        "USER_FIRST_NAME",                      // 0x48 /* Access value token */
+        "USER_ID",                              // 0x49 /* Access value token */
+        "USER_LAST_NAME",                       // 0x4a /* Access value token */
+        "USER_MOBILE_NUMBER",                   // 0x4b /* Access value token */
+        "USER_ONLINE_STATUS",                   // 0x4c /* Access value token */
+        "WAPSMS",                               // 0x4d /* Access value token */
+        "WAPUDP",                               // 0x4e /* Access value token */
+        "WSP",                                  // 0x4f /* Access value token */
+        "GROUP_USER_ID_AUTOJOIN",               // 0x50 /* Access value token */ /* WV 1.2 */
+        null,                                   // 0x51
+        null,                                   // 0x52
+        null,                                   // 0x53
+        null,                                   // 0x54
+        null,                                   // 0x55
+        null,                                   // 0x56
+        null,                                   // 0x57
+        null,                                   // 0x58
+        null,                                   // 0x59
+        null,                                   // 0x5a
+        "ANGRY",                                // 0x5b /* Presence value token */
+        "ANXIOUS",                              // 0x5c /* Presence value token */
+        "ASHAMED",                              // 0x5d /* Presence value token */
+        "AUDIO_CALL",                           // 0x5e /* Presence value token */
+        "AVAILABLE",                            // 0x5f /* Presence value token */
+        "BORED",                                // 0x60 /* Presence value token */
+        "CALL",                                 // 0x61 /* Presence value token */
+        "CLI",                                  // 0x62 /* Presence value token */
+        "COMPUTER",                             // 0x63 /* Presence value token */
+        "DISCREET",                             // 0x64 /* Presence value token */
+        "EMAIL",                                // 0x65 /* Presence value token */
+        "EXCITED",                              // 0x66 /* Presence value token */
+        "HAPPY",                                // 0x67 /* Presence value token */
+        "IM",                                   // 0x68 /* Presence value token */
+        "IM_OFFLINE",                           // 0x69 /* Presence value token */
+        "IM_ONLINE",                            // 0x6a /* Presence value token */
+        "IN_LOVE",                              // 0x6b /* Presence value token */
+        "INVINCIBLE",                           // 0x6c /* Presence value token */
+        "JEALOUS",                              // 0x6d /* Presence value token */
+        "MMS",                                  // 0x6e /* Presence value token */
+        "MOBILE_PHONE",                         // 0x6f /* Presence value token */
+        "NOT_AVAILABLE",                        // 0x70 /* Presence value token */
+        "OTHER",                                // 0x71 /* Presence value token */
+        "PDA",                                  // 0x72 /* Presence value token */
+        "SAD",                                  // 0x73 /* Presence value token */
+        "SLEEPY",                               // 0x74 /* Presence value token */
+        "SMS",                                  // 0x75 /* Presence value token */
+        "VIDEO_CALL",                           // 0x76 /* Presence value token */
+        "VIDEO_STREAM",                         // 0x77 /* Presence value token */
+    };
+    
+    
+}