Thursday, June 19, 2008

KXML skip SubTree skip Depth

KXML is just the name of XML parsing in J2ME. As I am using it in MiniIM, parsing XMPP and even special markup, I found it useful to skip upto a specific depth of XML tree. Note, the skipSubTree() method in KXMLParser / XMLPullParser API is useful. But it needs that you call it at tag start otherwise it will throw exception. So I introduce skipDepth(depth) idea. You just set your depth upto which you want to skip. It will just eat data upto that.

For example here is a small part of stream,

<?xml version='1.0'?>

<stream:stream xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" id="1523376107" from="jabber.org" version="1.0" xml:lang="en">

...
...
<stream:features>
<mechanisms xmlns="urn:ietf:params:xml:ns:xmpp-sasl">
<mechanism>DIGEST-MD5</mechanism>
<mechanism>PLAIN</mechanism>
</mechanisms>
<register xmlns="http://jabber.org/features/iq-register"/>
</stream:features>

...
...

Here is the simple code to parse the "stream:features",

final void readFeatures() throws IOException {
String tagName;
String text;
parser.nextTag();
parser.require(KXmlParser.START_TAG, null, PREFIX_FEATURES);

for (; parser.nextTag() != KXmlParser.END_TAG; parser.skipDepth(3)) { /* The features tag is placed at depth 1 */
/* we are parsing things inside this feature tag */
tagName = parser.getName();
if (tagName == null) {
throw new IllegalStateException("feature is null");
} else if (tagName.equals(STARTTLS)) {
/* set a starttls read command */
SimpleLogger.debug(this, "<" + STARTTLS);
featuresTLS = true;
} else if (tagName.equals(MECHANISMS)) {
/* traverse the mechanisms inside */
SimpleLogger.info(this, "readFeatures()\t\tparsing mechanisms");
/* so while inside the mechanisms */
for (; parser.nextTag() != KXmlParser.END_TAG;parser.skipDepth(5)) {
tagName = parser.getName();
if (tagName.equals(MECHANISM)) {
/* see if the mechanism is PLAIN */
text = expectText();
if (text == null) {

} else if (text.equals(PLAIN)) {
/* say that plain mechanism is found */
SimpleLogger.info(this, "<" + PLAIN);
featuresPlain = true;
} else if(text.equals(DIGEST_MD5)) {
SimpleLogger.info(this, "<" + DIGEST_MD5);
featuresDigestMd5 = true;
}
/* do not break, go on parsing mechanisms */
}
}
} else if (tagName.equals(BIND)) {
SimpleLogger.debug(this, "<" + BIND);
featuresBind = true;
} else if(tagName.equals(COMPRESSION)) {
// QUOTE "the ZLIB compression algorithm is mandatory-to-implement"
featuresZLIB = true;
/* traverse the mechanisms inside */
SimpleLogger.info(this, "readFeatures()\t\tparsing compression");
for (; parser.nextTag() != KXmlParser.END_TAG;parser.skipDepth(5)) {
tagName = parser.getName();
if (tagName.equals(METHOD)) {
String compressionMethod = expectText();
if(compressionMethod != null && compressionMethod.equals(ZLIB)) {
featuresZLIB = true;
}
}
}
} else {
SimpleLogger.debug(this, "<!" + tagName);
/* whatever it is just finish the tag */
}
SimpleLogger.debug(this, "< next");
}
parser.skipDepth(2);
}
}


And here is the small method I have added in KXMLParser.

public void skipDepth(int dep) throws IOException,XmlPullParserException {
int level = getDepth() - dep;
int eventType = getEventType();

if(eventType != KXmlParser.END_TAG) {
level++;
}

while(level > 0) {
eventType = next();
if (eventType == KXmlParser.END_TAG) {
--level;
}
else if (eventType == KXmlParser.START_TAG) {
++level;
}
}
}


I hope it helps :) ..

No comments: