0001: /* Copyright (c) 2002,2003,2004 Stefan Haustein, Oberhausen, Rhld., Germany
0002: *
0003: * Permission is hereby granted, free of charge, to any person obtaining a copy
0004: * of this software and associated documentation files (the "Software"), to deal
0005: * in the Software without restriction, including without limitation the rights
0006: * to use, copy, modify, merge, publish, distribute, sublicense, and/or
0007: * sell copies of the Software, and to permit persons to whom the Software is
0008: * furnished to do so, subject to the following conditions:
0009: *
0010: * The above copyright notice and this permission notice shall be included in
0011: * all copies or substantial portions of the Software.
0012: *
0013: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0014: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0015: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0016: * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0017: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
0018: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
0019: * IN THE SOFTWARE. */
0020:
0021: // Contributors: Bjorn Aadland, Chris Bartley, Nicola Fankhauser,
0022: // Victor Havin, Christian Kurzke, Bogdan Onoiu,
0023: // Elias Ross, Jain Sanjay, David Santoro.
0024: package org.kxml2.wap;
0025:
0026: import java.io.*;
0027: import java.util.Vector;
0028: import java.util.Hashtable;
0029:
0030: import org.xmlpull.v1.*;
0031:
0032: public class WbxmlParser implements XmlPullParser {
0033:
0034: static final String HEX_DIGITS = "0123456789abcdef";
0035:
0036: /** Parser event type for Wbxml-specific events. The Wbxml event code can be
0037: * accessed with getWapCode() */
0038:
0039: public static final int WAP_EXTENSION = 64;
0040:
0041: static final private String UNEXPECTED_EOF = "Unexpected EOF";
0042: static final private String ILLEGAL_TYPE = "Wrong event type";
0043:
0044: private InputStream in;
0045:
0046: private int TAG_TABLE = 0;
0047: private int ATTR_START_TABLE = 1;
0048: private int ATTR_VALUE_TABLE = 2;
0049:
0050: private String[] attrStartTable;
0051: private String[] attrValueTable;
0052: private String[] tagTable;
0053: private byte[] stringTable;
0054: private Hashtable cacheStringTable = null;
0055: private boolean processNsp;
0056:
0057: private int depth;
0058: private String[] elementStack = new String[16];
0059: private String[] nspStack = new String[8];
0060: private int[] nspCounts = new int[4];
0061:
0062: private int attributeCount;
0063: private String[] attributes = new String[16];
0064: private int nextId = -2;
0065:
0066: private Vector tables = new Vector();
0067:
0068: private int version;
0069: private int publicIdentifierId;
0070:
0071: // StartTag current;
0072: // ParseEvent next;
0073:
0074: private String prefix;
0075: private String namespace;
0076: private String name;
0077: private String text;
0078:
0079: private Object wapExtensionData;
0080: private int wapCode;
0081:
0082: private int type;
0083:
0084: private boolean degenerated;
0085: private boolean isWhitespace;
0086: private String encoding;
0087:
0088: public boolean getFeature(String feature) {
0089: if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature))
0090: return processNsp;
0091: else
0092: return false;
0093: }
0094:
0095: public String getInputEncoding() {
0096: return encoding;
0097: }
0098:
0099: public void defineEntityReplacementText(String entity, String value)
0100: throws XmlPullParserException {
0101:
0102: // just ignore, has no effect
0103: }
0104:
0105: public Object getProperty(String property) {
0106: return null;
0107: }
0108:
0109: public int getNamespaceCount(int depth) {
0110: if (depth > this .depth)
0111: throw new IndexOutOfBoundsException();
0112: return nspCounts[depth];
0113: }
0114:
0115: public String getNamespacePrefix(int pos) {
0116: return nspStack[pos << 1];
0117: }
0118:
0119: public String getNamespaceUri(int pos) {
0120: return nspStack[(pos << 1) + 1];
0121: }
0122:
0123: public String getNamespace(String prefix) {
0124:
0125: if ("xml".equals(prefix))
0126: return "http://www.w3.org/XML/1998/namespace";
0127: if ("xmlns".equals(prefix))
0128: return "http://www.w3.org/2000/xmlns/";
0129:
0130: for (int i = (getNamespaceCount(depth) << 1) - 2; i >= 0; i -= 2) {
0131: if (prefix == null) {
0132: if (nspStack[i] == null)
0133: return nspStack[i + 1];
0134: } else if (prefix.equals(nspStack[i]))
0135: return nspStack[i + 1];
0136: }
0137: return null;
0138: }
0139:
0140: public int getDepth() {
0141: return depth;
0142: }
0143:
0144: public String getPositionDescription() {
0145:
0146: StringBuffer buf = new StringBuffer(
0147: type < TYPES.length ? TYPES[type] : "unknown");
0148: buf.append(' ');
0149:
0150: if (type == START_TAG || type == END_TAG) {
0151: if (degenerated)
0152: buf.append("(empty) ");
0153: buf.append('<');
0154: if (type == END_TAG)
0155: buf.append('/');
0156:
0157: if (prefix != null)
0158: buf.append("{" + namespace + "}" + prefix + ":");
0159: buf.append(name);
0160:
0161: int cnt = attributeCount << 2;
0162: for (int i = 0; i < cnt; i += 4) {
0163: buf.append(' ');
0164: if (attributes[i + 1] != null)
0165: buf.append("{" + attributes[i] + "}"
0166: + attributes[i + 1] + ":");
0167: buf.append(attributes[i + 2] + "='" + attributes[i + 3]
0168: + "'");
0169: }
0170:
0171: buf.append('>');
0172: } else if (type == IGNORABLE_WHITESPACE)
0173: ;
0174: else if (type != TEXT)
0175: buf.append(getText());
0176: else if (isWhitespace)
0177: buf.append("(whitespace)");
0178: else {
0179: String text = getText();
0180: if (text.length() > 16)
0181: text = text.substring(0, 16) + "...";
0182: buf.append(text);
0183: }
0184:
0185: return buf.toString();
0186: }
0187:
0188: public int getLineNumber() {
0189: return -1;
0190: }
0191:
0192: public int getColumnNumber() {
0193: return -1;
0194: }
0195:
0196: public boolean isWhitespace() throws XmlPullParserException {
0197: if (type != TEXT && type != IGNORABLE_WHITESPACE
0198: && type != CDSECT)
0199: exception(ILLEGAL_TYPE);
0200: return isWhitespace;
0201: }
0202:
0203: public String getText() {
0204: return text;
0205: }
0206:
0207: public char[] getTextCharacters(int[] poslen) {
0208: if (type >= TEXT) {
0209: poslen[0] = 0;
0210: poslen[1] = text.length();
0211: char[] buf = new char[text.length()];
0212: text.getChars(0, text.length(), buf, 0);
0213: return buf;
0214: }
0215:
0216: poslen[0] = -1;
0217: poslen[1] = -1;
0218: return null;
0219: }
0220:
0221: public String getNamespace() {
0222: return namespace;
0223: }
0224:
0225: public String getName() {
0226: return name;
0227: }
0228:
0229: public String getPrefix() {
0230: return prefix;
0231: }
0232:
0233: public boolean isEmptyElementTag() throws XmlPullParserException {
0234: if (type != START_TAG)
0235: exception(ILLEGAL_TYPE);
0236: return degenerated;
0237: }
0238:
0239: public int getAttributeCount() {
0240: return attributeCount;
0241: }
0242:
0243: public String getAttributeType(int index) {
0244: return "CDATA";
0245: }
0246:
0247: public boolean isAttributeDefault(int index) {
0248: return false;
0249: }
0250:
0251: public String getAttributeNamespace(int index) {
0252: if (index >= attributeCount)
0253: throw new IndexOutOfBoundsException();
0254: return attributes[index << 2];
0255: }
0256:
0257: public String getAttributeName(int index) {
0258: if (index >= attributeCount)
0259: throw new IndexOutOfBoundsException();
0260: return attributes[(index << 2) + 2];
0261: }
0262:
0263: public String getAttributePrefix(int index) {
0264: if (index >= attributeCount)
0265: throw new IndexOutOfBoundsException();
0266: return attributes[(index << 2) + 1];
0267: }
0268:
0269: public String getAttributeValue(int index) {
0270: if (index >= attributeCount)
0271: throw new IndexOutOfBoundsException();
0272: return attributes[(index << 2) + 3];
0273: }
0274:
0275: public String getAttributeValue(String namespace, String name) {
0276:
0277: for (int i = (attributeCount << 2) - 4; i >= 0; i -= 4) {
0278: if (attributes[i + 2].equals(name)
0279: && (namespace == null || attributes[i]
0280: .equals(namespace)))
0281: return attributes[i + 3];
0282: }
0283:
0284: return null;
0285: }
0286:
0287: public int getEventType() throws XmlPullParserException {
0288: return type;
0289: }
0290:
0291: // TODO: Reuse resolveWapExtension here? Raw Wap extensions would still be accessible
0292: // via nextToken(); ....?
0293:
0294: public int next() throws XmlPullParserException, IOException {
0295:
0296: isWhitespace = true;
0297: int minType = 9999;
0298:
0299: while (true) {
0300:
0301: String save = text;
0302:
0303: nextImpl();
0304:
0305: if (type < minType)
0306: minType = type;
0307:
0308: if (minType > CDSECT)
0309: continue; // no "real" event so far
0310:
0311: if (minType >= TEXT) { // text, see if accumulate
0312:
0313: if (save != null)
0314: text = text == null ? save : save + text;
0315:
0316: switch (peekId()) {
0317: case Wbxml.ENTITY:
0318: case Wbxml.STR_I:
0319: case Wbxml.STR_T:
0320: case Wbxml.LITERAL:
0321: case Wbxml.LITERAL_C:
0322: case Wbxml.LITERAL_A:
0323: case Wbxml.LITERAL_AC:
0324: continue;
0325: }
0326: }
0327:
0328: break;
0329: }
0330:
0331: type = minType;
0332:
0333: if (type > TEXT)
0334: type = TEXT;
0335:
0336: return type;
0337: }
0338:
0339: public int nextToken() throws XmlPullParserException, IOException {
0340:
0341: isWhitespace = true;
0342: nextImpl();
0343: return type;
0344: }
0345:
0346: public int nextTag() throws XmlPullParserException, IOException {
0347:
0348: next();
0349: if (type == TEXT && isWhitespace)
0350: next();
0351:
0352: if (type != END_TAG && type != START_TAG)
0353: exception("unexpected type");
0354:
0355: return type;
0356: }
0357:
0358: public String nextText() throws XmlPullParserException, IOException {
0359: if (type != START_TAG)
0360: exception("precondition: START_TAG");
0361:
0362: next();
0363:
0364: String result;
0365:
0366: if (type == TEXT) {
0367: result = getText();
0368: next();
0369: } else
0370: result = "";
0371:
0372: if (type != END_TAG)
0373: exception("END_TAG expected");
0374:
0375: return result;
0376: }
0377:
0378: public void require(int type, String namespace, String name)
0379: throws XmlPullParserException, IOException {
0380:
0381: if (type != this .type
0382: || (namespace != null && !namespace
0383: .equals(getNamespace()))
0384: || (name != null && !name.equals(getName())))
0385: exception("expected: "
0386: + (type == WAP_EXTENSION ? "WAP Ext."
0387: : (TYPES[type] + " {" + namespace + "}" + name)));
0388: }
0389:
0390: public void setInput(Reader reader) throws XmlPullParserException {
0391: exception("InputStream required");
0392: }
0393:
0394: public void setInput(InputStream in, String enc)
0395: throws XmlPullParserException {
0396:
0397: this .in = in;
0398:
0399: try {
0400: version = readByte();
0401: publicIdentifierId = readInt();
0402:
0403: if (publicIdentifierId == 0)
0404: readInt();
0405:
0406: int charset = readInt(); // skip charset
0407:
0408: if (null == enc) {
0409: switch (charset) {
0410: case 4:
0411: encoding = "ISO-8859-1";
0412: break;
0413: case 106:
0414: encoding = "UTF-8";
0415: break;
0416: // add more if you need them
0417: // http://www.iana.org/assignments/character-sets
0418: // case MIBenum: encoding = Name break;
0419: default:
0420: throw new UnsupportedEncodingException("" + charset);
0421: }
0422: } else {
0423: encoding = enc;
0424: }
0425:
0426: int strTabSize = readInt();
0427: stringTable = new byte[strTabSize];
0428:
0429: int ok = 0;
0430: while (ok < strTabSize) {
0431: int cnt = in.read(stringTable, ok, strTabSize - ok);
0432: if (cnt <= 0)
0433: break;
0434: ok += cnt;
0435: }
0436:
0437: selectPage(0, true);
0438: selectPage(0, false);
0439: } catch (IOException e) {
0440: exception("Illegal input format");
0441: }
0442: }
0443:
0444: public void setFeature(String feature, boolean value)
0445: throws XmlPullParserException {
0446: if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature))
0447: processNsp = value;
0448: else
0449: exception("unsupported feature: " + feature);
0450: }
0451:
0452: public void setProperty(String property, Object value)
0453: throws XmlPullParserException {
0454: throw new XmlPullParserException("unsupported property: "
0455: + property);
0456: }
0457:
0458: // ---------------------- private / internal methods
0459:
0460: private final boolean adjustNsp() throws XmlPullParserException {
0461:
0462: boolean any = false;
0463:
0464: for (int i = 0; i < attributeCount << 2; i += 4) {
0465: // * 4 - 4; i >= 0; i -= 4) {
0466:
0467: String attrName = attributes[i + 2];
0468: int cut = attrName.indexOf(':');
0469: String prefix;
0470:
0471: if (cut != -1) {
0472: prefix = attrName.substring(0, cut);
0473: attrName = attrName.substring(cut + 1);
0474: } else if (attrName.equals("xmlns")) {
0475: prefix = attrName;
0476: attrName = null;
0477: } else
0478: continue;
0479:
0480: if (!prefix.equals("xmlns")) {
0481: any = true;
0482: } else {
0483: int j = (nspCounts[depth]++) << 1;
0484:
0485: nspStack = ensureCapacity(nspStack, j + 2);
0486: nspStack[j] = attrName;
0487: nspStack[j + 1] = attributes[i + 3];
0488:
0489: if (attrName != null && attributes[i + 3].equals(""))
0490: exception("illegal empty namespace");
0491:
0492: // prefixMap = new PrefixMap (prefixMap, attrName, attr.getValue ());
0493:
0494: //System.out.println (prefixMap);
0495:
0496: System.arraycopy(attributes, i + 4, attributes, i,
0497: ((--attributeCount) << 2) - i);
0498:
0499: i -= 4;
0500: }
0501: }
0502:
0503: if (any) {
0504: for (int i = (attributeCount << 2) - 4; i >= 0; i -= 4) {
0505:
0506: String attrName = attributes[i + 2];
0507: int cut = attrName.indexOf(':');
0508:
0509: if (cut == 0)
0510: throw new RuntimeException(
0511: "illegal attribute name: " + attrName
0512: + " at " + this );
0513:
0514: else if (cut != -1) {
0515: String attrPrefix = attrName.substring(0, cut);
0516:
0517: attrName = attrName.substring(cut + 1);
0518:
0519: String attrNs = getNamespace(attrPrefix);
0520:
0521: if (attrNs == null)
0522: throw new RuntimeException("Undefined Prefix: "
0523: + attrPrefix + " in " + this );
0524:
0525: attributes[i] = attrNs;
0526: attributes[i + 1] = attrPrefix;
0527: attributes[i + 2] = attrName;
0528:
0529: for (int j = (attributeCount << 2) - 4; j > i; j -= 4)
0530: if (attrName.equals(attributes[j + 2])
0531: && attrNs.equals(attributes[j]))
0532: exception("Duplicate Attribute: {" + attrNs
0533: + "}" + attrName);
0534: }
0535: }
0536: }
0537:
0538: int cut = name.indexOf(':');
0539:
0540: if (cut == 0)
0541: exception("illegal tag name: " + name);
0542: else if (cut != -1) {
0543: prefix = name.substring(0, cut);
0544: name = name.substring(cut + 1);
0545: }
0546:
0547: this .namespace = getNamespace(prefix);
0548:
0549: if (this .namespace == null) {
0550: if (prefix != null)
0551: exception("undefined prefix: " + prefix);
0552: this .namespace = NO_NAMESPACE;
0553: }
0554:
0555: return any;
0556: }
0557:
0558: private final void setTable(int page, int type, String[] table) {
0559: if (stringTable != null) {
0560: throw new RuntimeException(
0561: "setXxxTable must be called before setInput!");
0562: }
0563: while (tables.size() < 3 * page + 3) {
0564: tables.addElement(null);
0565: }
0566: tables.setElementAt(table, page * 3 + type);
0567: }
0568:
0569: private final void exception(String desc)
0570: throws XmlPullParserException {
0571: throw new XmlPullParserException(desc, this , null);
0572: }
0573:
0574: private void selectPage(int nr, boolean tags)
0575: throws XmlPullParserException {
0576: if (tables.size() == 0 && nr == 0)
0577: return;
0578:
0579: if (nr * 3 > tables.size())
0580: exception("Code Page " + nr + " undefined!");
0581:
0582: if (tags)
0583: tagTable = (String[]) tables.elementAt(nr * 3 + TAG_TABLE);
0584: else {
0585: attrStartTable = (String[]) tables.elementAt(nr * 3
0586: + ATTR_START_TABLE);
0587: attrValueTable = (String[]) tables.elementAt(nr * 3
0588: + ATTR_VALUE_TABLE);
0589: }
0590: }
0591:
0592: private final void nextImpl() throws IOException,
0593: XmlPullParserException {
0594:
0595: String s;
0596:
0597: if (type == END_TAG) {
0598: depth--;
0599: }
0600:
0601: if (degenerated) {
0602: type = XmlPullParser.END_TAG;
0603: degenerated = false;
0604: return;
0605: }
0606:
0607: text = null;
0608: prefix = null;
0609: name = null;
0610:
0611: int id = peekId();
0612: while (id == Wbxml.SWITCH_PAGE) {
0613: nextId = -2;
0614: selectPage(readByte(), true);
0615: id = peekId();
0616: }
0617: nextId = -2;
0618:
0619: switch (id) {
0620: case -1:
0621: type = XmlPullParser.END_DOCUMENT;
0622: break;
0623:
0624: case Wbxml.END: {
0625: int sp = (depth - 1) << 2;
0626:
0627: type = END_TAG;
0628: namespace = elementStack[sp];
0629: prefix = elementStack[sp + 1];
0630: name = elementStack[sp + 2];
0631: }
0632: break;
0633:
0634: case Wbxml.ENTITY: {
0635: type = ENTITY_REF;
0636: char c = (char) readInt();
0637: text = "" + c;
0638: name = "#" + ((int) c);
0639: }
0640:
0641: break;
0642:
0643: case Wbxml.STR_I:
0644: type = TEXT;
0645: text = readStrI();
0646: break;
0647:
0648: case Wbxml.EXT_I_0:
0649: case Wbxml.EXT_I_1:
0650: case Wbxml.EXT_I_2:
0651: case Wbxml.EXT_T_0:
0652: case Wbxml.EXT_T_1:
0653: case Wbxml.EXT_T_2:
0654: case Wbxml.EXT_0:
0655: case Wbxml.EXT_1:
0656: case Wbxml.EXT_2:
0657: case Wbxml.OPAQUE:
0658:
0659: type = WAP_EXTENSION;
0660: wapCode = id;
0661: wapExtensionData = parseWapExtension(id);
0662: break;
0663:
0664: case Wbxml.PI:
0665: throw new RuntimeException("PI curr. not supp.");
0666: // readPI;
0667: // break;
0668:
0669: case Wbxml.STR_T: {
0670: type = TEXT;
0671: text = readStrT();
0672: }
0673: break;
0674:
0675: default:
0676: parseElement(id);
0677: }
0678: // }
0679: // while (next == null);
0680:
0681: // return next;
0682: }
0683:
0684: /** Overwrite this method to intercept all wap events */
0685:
0686: public Object parseWapExtension(int id) throws IOException,
0687: XmlPullParserException {
0688:
0689: switch (id) {
0690: case Wbxml.EXT_I_0:
0691: case Wbxml.EXT_I_1:
0692: case Wbxml.EXT_I_2:
0693: return readStrI();
0694:
0695: case Wbxml.EXT_T_0:
0696: case Wbxml.EXT_T_1:
0697: case Wbxml.EXT_T_2:
0698: return new Integer(readInt());
0699:
0700: case Wbxml.EXT_0:
0701: case Wbxml.EXT_1:
0702: case Wbxml.EXT_2:
0703: return null;
0704:
0705: case Wbxml.OPAQUE: {
0706: int count = readInt();
0707: byte[] buf = new byte[count];
0708:
0709: while (count > 0) {
0710: count -= in.read(buf, buf.length - count, count);
0711: }
0712:
0713: return buf;
0714: } // case OPAQUE
0715:
0716: default:
0717: exception("illegal id: " + id);
0718: return null; // dead code
0719: } // SWITCH
0720: }
0721:
0722: public void readAttr() throws IOException, XmlPullParserException {
0723:
0724: int id = readByte();
0725: int i = 0;
0726:
0727: while (id != 1) {
0728:
0729: while (id == Wbxml.SWITCH_PAGE) {
0730: selectPage(readByte(), false);
0731: id = readByte();
0732: }
0733:
0734: String name = resolveId(attrStartTable, id);
0735: StringBuffer value;
0736:
0737: int cut = name.indexOf('=');
0738:
0739: if (cut == -1)
0740: value = new StringBuffer();
0741: else {
0742: value = new StringBuffer(name.substring(cut + 1));
0743: name = name.substring(0, cut);
0744: }
0745:
0746: id = readByte();
0747: while (id > 128 || id == Wbxml.SWITCH_PAGE
0748: || id == Wbxml.ENTITY || id == Wbxml.STR_I
0749: || id == Wbxml.STR_T
0750: || (id >= Wbxml.EXT_I_0 && id <= Wbxml.EXT_I_2)
0751: || (id >= Wbxml.EXT_T_0 && id <= Wbxml.EXT_T_2)) {
0752:
0753: switch (id) {
0754: case Wbxml.SWITCH_PAGE:
0755: selectPage(readByte(), false);
0756: break;
0757:
0758: case Wbxml.ENTITY:
0759: value.append((char) readInt());
0760: break;
0761:
0762: case Wbxml.STR_I:
0763: value.append(readStrI());
0764: break;
0765:
0766: case Wbxml.EXT_I_0:
0767: case Wbxml.EXT_I_1:
0768: case Wbxml.EXT_I_2:
0769: case Wbxml.EXT_T_0:
0770: case Wbxml.EXT_T_1:
0771: case Wbxml.EXT_T_2:
0772: case Wbxml.EXT_0:
0773: case Wbxml.EXT_1:
0774: case Wbxml.EXT_2:
0775: case Wbxml.OPAQUE:
0776: value.append(resolveWapExtension(id,
0777: parseWapExtension(id)));
0778: break;
0779:
0780: case Wbxml.STR_T:
0781: value.append(readStrT());
0782: break;
0783:
0784: default:
0785: value.append(resolveId(attrValueTable, id));
0786: }
0787:
0788: id = readByte();
0789: }
0790:
0791: attributes = ensureCapacity(attributes, i + 4);
0792:
0793: attributes[i++] = "";
0794: attributes[i++] = null;
0795: attributes[i++] = name;
0796: attributes[i++] = value.toString();
0797:
0798: attributeCount++;
0799: }
0800: }
0801:
0802: private int peekId() throws IOException {
0803: if (nextId == -2) {
0804: nextId = in.read();
0805: }
0806: return nextId;
0807: }
0808:
0809: /** overwrite for own WAP extension handling in attributes and high level parsing
0810: * (above nextToken() level) */
0811:
0812: protected String resolveWapExtension(int id, Object data) {
0813:
0814: if (data instanceof byte[]) {
0815: StringBuffer sb = new StringBuffer();
0816: byte[] b = (byte[]) data;
0817:
0818: for (int i = 0; i < b.length; i++) {
0819: sb.append(HEX_DIGITS.charAt((b[i] >> 4) & 0x0f));
0820: sb.append(HEX_DIGITS.charAt(b[i] & 0x0f));
0821: }
0822: return sb.toString();
0823: }
0824:
0825: return "$(" + data + ")";
0826: }
0827:
0828: String resolveId(String[] tab, int id) throws IOException {
0829: int idx = (id & 0x07f) - 5;
0830: if (idx == -1) {
0831: wapCode = -1;
0832: return readStrT();
0833: }
0834: if (idx < 0 || tab == null || idx >= tab.length
0835: || tab[idx] == null)
0836: throw new IOException("id " + id + " undef.");
0837:
0838: wapCode = idx + 5;
0839:
0840: return tab[idx];
0841: }
0842:
0843: void parseElement(int id) throws IOException,
0844: XmlPullParserException {
0845:
0846: type = START_TAG;
0847: name = resolveId(tagTable, id & 0x03f);
0848:
0849: attributeCount = 0;
0850: if ((id & 128) != 0) {
0851: readAttr();
0852: }
0853:
0854: degenerated = (id & 64) == 0;
0855:
0856: int sp = depth++ << 2;
0857:
0858: // transfer to element stack
0859:
0860: elementStack = ensureCapacity(elementStack, sp + 4);
0861: elementStack[sp + 3] = name;
0862:
0863: if (depth >= nspCounts.length) {
0864: int[] bigger = new int[depth + 4];
0865: System.arraycopy(nspCounts, 0, bigger, 0, nspCounts.length);
0866: nspCounts = bigger;
0867: }
0868:
0869: nspCounts[depth] = nspCounts[depth - 1];
0870:
0871: for (int i = attributeCount - 1; i > 0; i--) {
0872: for (int j = 0; j < i; j++) {
0873: if (getAttributeName(i).equals(getAttributeName(j)))
0874: exception("Duplicate Attribute: "
0875: + getAttributeName(i));
0876: }
0877: }
0878:
0879: if (processNsp)
0880: adjustNsp();
0881: else
0882: namespace = "";
0883:
0884: elementStack[sp] = namespace;
0885: elementStack[sp + 1] = prefix;
0886: elementStack[sp + 2] = name;
0887:
0888: }
0889:
0890: private final String[] ensureCapacity(String[] arr, int required) {
0891: if (arr.length >= required)
0892: return arr;
0893: String[] bigger = new String[required + 16];
0894: System.arraycopy(arr, 0, bigger, 0, arr.length);
0895: return bigger;
0896: }
0897:
0898: int readByte() throws IOException {
0899: int i = in.read();
0900: if (i == -1)
0901: throw new IOException("Unexpected EOF");
0902: return i;
0903: }
0904:
0905: int readInt() throws IOException {
0906: int result = 0;
0907: int i;
0908:
0909: do {
0910: i = readByte();
0911: result = (result << 7) | (i & 0x7f);
0912: } while ((i & 0x80) != 0);
0913:
0914: return result;
0915: }
0916:
0917: String readStrI() throws IOException {
0918: ByteArrayOutputStream buf = new ByteArrayOutputStream();
0919: boolean wsp = true;
0920: while (true) {
0921: int i = in.read();
0922: if (i == 0) {
0923: break;
0924: }
0925: if (i == -1) {
0926: throw new IOException(UNEXPECTED_EOF);
0927: }
0928: if (i > 32) {
0929: wsp = false;
0930: }
0931: buf.write(i);
0932: }
0933: isWhitespace = wsp;
0934: String result = new String(buf.toByteArray(), encoding);
0935: buf.close();
0936: return result;
0937: }
0938:
0939: String readStrT() throws IOException {
0940: int pos = readInt();
0941: // As the main reason of stringTable is compression we build a cache of Strings
0942: // stringTable is supposed to help create Strings from parts which means some cache hit rate
0943: // This will help to minimize the Strings created when invoking readStrT() repeatedly
0944: if (cacheStringTable == null) {
0945: //Lazy init if device is not using StringTable but inline 0x03 strings
0946: cacheStringTable = new Hashtable();
0947: }
0948: String forReturn = (String) cacheStringTable.get(new Integer(
0949: pos));
0950: if (forReturn == null) {
0951:
0952: int end = pos;
0953: while (end < stringTable.length && stringTable[end] != '\0') {
0954: end++;
0955: }
0956: forReturn = new String(stringTable, pos, end - pos,
0957: encoding);
0958: cacheStringTable.put(new Integer(pos), forReturn);
0959: }
0960: return forReturn;
0961: }
0962:
0963: /**
0964: * Sets the tag table for a given page.
0965: * The first string in the array defines tag 5, the second tag 6 etc.
0966: */
0967:
0968: public void setTagTable(int page, String[] table) {
0969: setTable(page, TAG_TABLE, table);
0970:
0971: // this.tagTable = tagTable;
0972: // if (page != 0)
0973: // throw new RuntimeException("code pages curr. not supp.");
0974: }
0975:
0976: /** Sets the attribute start Table for a given page.
0977: * The first string in the array defines attribute
0978: * 5, the second attribute 6 etc. Please use the
0979: * character '=' (without quote!) as delimiter
0980: * between the attribute name and the (start of the) value
0981: */
0982:
0983: public void setAttrStartTable(int page, String[] table) {
0984:
0985: setTable(page, ATTR_START_TABLE, table);
0986: }
0987:
0988: /** Sets the attribute value Table for a given page.
0989: * The first string in the array defines attribute value 0x85,
0990: * the second attribute value 0x86 etc.
0991: */
0992:
0993: public void setAttrValueTable(int page, String[] table) {
0994:
0995: setTable(page, ATTR_VALUE_TABLE, table);
0996: }
0997:
0998: /** Returns the token ID for start tags or the event type for wap proprietary events
0999: * such as OPAQUE.
1000: */
1001:
1002: public int getWapCode() {
1003: return wapCode;
1004: }
1005:
1006: public Object getWapExtensionData() {
1007: return wapExtensionData;
1008: }
1009:
1010: }
|