logo       

Issues with WbxmlParser.java in KXML2 rel. 2.1.8: msg#00000

java.enhydra.kxml

Subject: Issues with WbxmlParser.java in KXML2 rel. 2.1.8

Hello,

I am using the WbxmlParser class in KXML2 2.1.8 and wasn't able to "roundtrip" my xml to wbxml and back. I found a few issues with the class that pushed me to debug it and figured out enough of those issues to make the parser work for the project I am working on.

I am not sure if this is the right forum to post these changes, but I am outputting the changed class for anybody who is experiencing the same problems with this class (look for "EV>>>" comments).

It'd be nice if the author of the class could look at those changes and let everyone know if they are correct or not. One of the issue got fixed by uncommented code that worked perfectly, so I am curious to know why was this code commented in the first place !

Anyway, here is the dump of the WbxmlParser class I've modified. Good luck!

Eric

/* 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: Bjorn Aadland

package org.kxml2.wap;

import java.io.*;

import org.xmlpull.v1.*;

/**
* PLEASE NOTE: Does not even compile yet
*
* Still Todo:
<ul>
<li>implement Processing Instructions</li>
<li>implement support for more than one codepages</li>
</ul>
*/

public class WbxmlParser implements XmlPullParser {

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 String[] attrStartTable;
private String[] attrValueTable;
private String[] tagTable;
private String stringTable;
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;


int version;
int publicIdentifierId;
int charSet;

// StartTag current;
// ParseEvent next;

private String prefix;
private String namespace;
private String name;
private String text;
// private String encoding;
private Object wapExtensionData;
private int wapExtensionCode;

private int type;

private boolean degenerated;
private boolean isWhitespace;

public boolean getFeature(String feature) {
if (XmlPullParser
.FEATURE_PROCESS_NAMESPACES
.equals(feature))
return processNsp;
else
return false;
}

public String getInputEncoding() {
// should return someting depending on charSet here!!!!!
return null;
}

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;
}

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.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: " + 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();

charSet = readInt(); // skip charset

int strTabSize = readInt();

StringBuffer buf = new StringBuffer(strTabSize);

for (int i = 0; i < strTabSize; i++)
buf.append((char) readByte());
stringTable = buf.toString();
}
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 exception(String desc)
throws XmlPullParserException {
throw new XmlPullParserException(desc, this, null);
}


private final void nextImpl()
throws IOException, XmlPullParserException {

String s;

if (type == END_TAG) {
depth--;
}

if (degenerated) {
// EV >>>
degenerated = false;
// <<< EV
type = XmlPullParser.END_TAG;
return;
}

text = null;
prefix = null;
name = null;

int id = peekId ();
nextId = -2;

switch (id) {
case -1 :
type = XmlPullParser.END_DOCUMENT;
break;

case Wbxml.SWITCH_PAGE :
if (readByte() != 0)
throw new IOException("Curr. only CP0 supported");
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 :
parseWapExtension(id);
break;

case Wbxml.PI :
throw new RuntimeException("PI curr. not supp.");
// readPI;
// break;

case Wbxml.STR_T :
{
type = TEXT;
int pos = readInt();
int end = stringTable.indexOf('\0', pos);
text = stringTable.substring(pos, end);
}
break;

default :
parseElement(id);
}
// }
// while (next == null);

// return next;
}

/** For handling wap extensions in attributes, overwrite this
method, call super and return a corresponding TextEvent. */

public void parseWapExtension(int id)
throws IOException, XmlPullParserException {

type = WAP_EXTENSION;
wapExtensionCode = id;

switch (id) {
case Wbxml.EXT_I_0 :
case Wbxml.EXT_I_1 :
case Wbxml.EXT_I_2 :
wapExtensionData = readStrI();
break;

case Wbxml.EXT_T_0 :
case Wbxml.EXT_T_1 :
case Wbxml.EXT_T_2 :
wapExtensionData = new Integer(readInt());
break;

case Wbxml.EXT_0 :
case Wbxml.EXT_1 :
case Wbxml.EXT_2 :
break;

case Wbxml.OPAQUE :
{
int len = readInt();
byte[] buf = new byte[len];

for (int i = 0;
i < len;
i++) // enhance with blockread!
buf[i] = (byte) readByte();

wapExtensionData = buf;
} // case OPAQUE
} // SWITCH

throw new IOException("illegal id!");
}

public void readAttr() throws IOException {

int id = readByte();
int i = 0;

while (id != 1) {

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.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.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 :

throw new RuntimeException("wap extension in attr not supported yet");

/*
ParseEvent e = parseWapExtension(id);
if (!(e.getType() != Xml.TEXT
&& e.getType() != Xml.WHITESPACE))
throw new RuntimeException("parse WapExtension must return Text Event in order to work inside Attributes!");

value.append(e.getText());

//value.append (handleExtension (id)); // skip EXT in ATTR
//break;
*/

case Wbxml.STR_T :
value.append(readStrT());
break;

default :
value.append(
resolveId(attrValueTable, id));
}

id = readByte();
}

attributes = ensureCapacity(attributes, i + 4);

// EV >>>
attributeCount++;
// <<< EV

attributes[i++] = "";
attributes[i++] = null;
attributes[i++] = name;
attributes[i++] = value.toString();
}

}

private int peekId () throws IOException {
if (nextId == -2) {
nextId = in.read ();
}
return nextId;
}




String resolveId(String[] tab, int id) throws IOException {
int idx = (id & 0x07f) - 5;
if (idx == -1)
return readStrT();
if (idx < 0
|| tab == null
|| idx >= tab.length
|| tab[idx] == null)
throw new IOException("id " + id + " undef.");

return tab[idx];
}

void parseElement(int id)
throws IOException, XmlPullParserException {

name = resolveId(tagTable, id & 0x03f);
type = START_TAG;

// EV >>>
attributeCount = 0;
// <<< EV

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;

// EV >>>
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];
// <<< EV

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 {
StringBuffer buf = new StringBuffer();
boolean wsp = true;
while (true) {
int i = in.read();
if (i == -1)
throw new IOException("Unexpected EOF");
if (i == 0)
break;
if (i > 32)
wsp = false;
buf.append((char) i);
}
isWhitespace = wsp;
return buf.toString();
}

String readStrT() throws IOException {
int pos = readInt();
int end = stringTable.indexOf('\0', pos);

return stringTable.substring(pos, end);
}

/** Sets the tag table for a given page.
* The first string in the array defines tag 5, the second tag 6 etc.
* Currently, only page 0 is supported
*/

public void setTagTable(int page, String[] tagTable) {
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.
* Currently, only page 0 is supported. 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) {
this.attrStartTable = attrStartTable;
if (page != 0)
throw new RuntimeException("code pages curr. not supp.");
}

/** 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.
* Currently, only page 0 is supported.
*/

public void setAttrValueTable(
int page,
String[] attrStartTable) {
this.attrValueTable = attrStartTable;
if (page != 0)
throw new RuntimeException("code pages curr. not supp.");
}

}

_________________________________________________________________
Fretting that your Hotmail account may expire because you forgot to sign in enough? Get Hotmail Extra Storage today! http://join.msn.com/?PAGE=features/es


<Prev in Thread] Current Thread [Next in Thread>
Google Custom Search

News | FAQ | advertise