1ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/* 2ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Copyright (C) 2008-2009 Marc Blank 3ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Licensed to The Android Open Source Project. 4ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 5ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Licensed under the Apache License, Version 2.0 (the "License"); 6ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * you may not use this file except in compliance with the License. 7ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * You may obtain a copy of the License at 8ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 9ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * http://www.apache.org/licenses/LICENSE-2.0 10ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 11ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Unless required by applicable law or agreed to in writing, software 12ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * distributed under the License is distributed on an "AS IS" BASIS, 13ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * See the License for the specific language governing permissions and 15ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * limitations under the License. 16ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 17ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 18ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankpackage com.android.exchange.adapter; 19ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 2045fed27dbea77923779f6142c0341b3c80a77eddMarc Blankimport android.content.Context; 2145fed27dbea77923779f6142c0341b3c80a77eddMarc Blank 227c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport com.android.exchange.Eas; 23ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport com.android.exchange.EasException; 243c193c425ff383dbc94514d055ae6778821d8471Martin Hibdonimport com.android.exchange.service.EasService; 25c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blankimport com.android.exchange.utility.FileLogger; 26942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedyimport com.android.mail.utils.LogUtils; 2745fed27dbea77923779f6142c0341b3c80a77eddMarc Blankimport com.google.common.annotations.VisibleForTesting; 28ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 2961563f075e07b9961ede4000d815865edc0f2497Marc Blankimport java.io.ByteArrayOutputStream; 307c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport java.io.FileNotFoundException; 317c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport java.io.FileOutputStream; 327c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport java.io.IOException; 337c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport java.io.InputStream; 343d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shraunerimport java.util.ArrayDeque; 357c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport java.util.ArrayList; 363d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shraunerimport java.util.Arrays; 373d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shraunerimport java.util.Deque; 387c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank 39ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/** 40ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Extremely fast and lightweight WBXML parser, implementing only the subset of WBXML that 413d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * EAS uses (as defined in the EAS specification). 423d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * 433d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * Supports: 443d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * WBXML tokens to encode XML tags 453d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * WBXML code pages to support multiple XML namespaces 463d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * Inline strings 473d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * Opaque data 483d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * 493d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * Does not support: (throws EasParserException) 503d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * String tables 513d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * Entities 523d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * Processing instructions 533d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * Attribute encoding 54ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 55ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 567c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankpublic abstract class Parser { 5734d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank private static final boolean LOG_VERBOSE = false; 5834d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank 59110837ebff288a75f9bda067c38e2c46797d99b5Alon Albert private static final String LOG_TAG = Eas.LOG_TAG; 60ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 61ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The following constants are Wbxml standard 62ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public static final int START_DOCUMENT = 0; 635bad4cfbe9a6401d5b4cde5bb8b4eec8d5fab82fJay Shrauner public static final int END_DOCUMENT = 1; 645bad4cfbe9a6401d5b4cde5bb8b4eec8d5fab82fJay Shrauner private static final int DONE = 1; 655bad4cfbe9a6401d5b4cde5bb8b4eec8d5fab82fJay Shrauner private static final int START = 2; 66ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public static final int END = 3; 675bad4cfbe9a6401d5b4cde5bb8b4eec8d5fab82fJay Shrauner private static final int TEXT = 4; 685bad4cfbe9a6401d5b4cde5bb8b4eec8d5fab82fJay Shrauner private static final int OPAQUE = 5; 69ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private static final int NOT_ENDED = Integer.MIN_VALUE; 70ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private static final int EOF_BYTE = -1; 7126d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 72ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu private boolean capture = false; 73ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 74ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private ArrayList<Integer> captureArray; 75ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 76ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The input stream for this parser 77ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private InputStream in; 78ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 79ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The stack of names of tags being processed; used when debug = true 80ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private String[] nameArray = new String[32]; 81ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 823d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner public class Tag { 833d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner private final int mPage; 843d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner private final int mIndex; 853d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner // Whether the tag is associated with content (a value) 863d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner public final boolean mNoContent; 873d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner private final String mName; 883d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner 893d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner public Tag(final int page, final int id) { 903d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner mPage = page; 913d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner // The tag is in the low 6 bits 923d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner mIndex = id & Tags.PAGE_MASK; 933d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner // If the high bit is set, there is content (a value) to be read 943d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner mNoContent = (id & Wbxml.WITH_CONTENT) == 0; 953d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner if (Tags.isGlobalTag(mIndex)) { 963d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner mName = "unsupported-WBXML"; 973d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner } else if (!Tags.isValidTag(mPage, mIndex)) { 983d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner mName = "unknown"; 993d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } else { 1003d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner mName = Tags.getTagName(mPage, mIndex); 1013d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } 1023d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } 1033d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner 1043d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner public int getTagNum() { 1054d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner if (Tags.isGlobalTag(mIndex)) { 1064d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner return mIndex; 1074d07dd49522f0573cb6ea1920f072bf677ed60f9Jay Shrauner } 1083d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner return (mPage << Tags.PAGE_SHIFT) | mIndex; 1093d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner } 1103d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner 1113d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner @Override 1123d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner public String toString() { 1133d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner return mName; 1143d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner } 1153d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } 1163d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner 117ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The stack of tags being processed 1183d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner private final Deque<Tag> startTagArray = new ArrayDeque<Tag>(); 119ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1203d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner private Tag startTag; 121ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1223d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner // The type of the last token read (eg, TEXT, OPAQUE, END, etc). 1233d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner private int type; 124ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1253d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner // The current page. As of EAS 14.1, this is a value 0-24. 1263d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner private int page; 127ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1283d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner // The current tag. The low order 6 bits contain the tag index and the 1293d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner // higher order bits the page number. The format matches that used for 1303d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner // the tag enums defined in Tags.java. 131ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public int tag; 132ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 133ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Whether the current tag is associated with content (a value) 134ae073cce37583f350ee4f1df4847c9c26eadb404Marc Blank public boolean noContent; 135ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1363d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner // The value read, as a String 1373d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner private String text; 138ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 13977186bb1a174432ef272584374942d8b9228e39cMarc Blank // The value read, as bytes 1403d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner private byte[] bytes; 14177186bb1a174432ef272584374942d8b9228e39cMarc Blank 142ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu // TODO: Define a new parse exception type rather than lumping these in as IOExceptions. 143ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 144bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank /** 145bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank * Generated when the parser comes to EOF prematurely during parsing (i.e. in error) 146bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank */ 147ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public class EofException extends IOException { 148ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private static final long serialVersionUID = 1L; 149ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 150ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 151bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank /** 152bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank * An EmptyStreamException is an EofException that occurs reading the first byte in the parser's 153bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank * input stream; in other words, the stream had no content. 154bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank */ 155bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank public class EmptyStreamException extends EofException { 156bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank private static final long serialVersionUID = 1L; 157bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } 158bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank 159ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public class EodException extends IOException { 160ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private static final long serialVersionUID = 1L; 161ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 162ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 163ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public class EasParserException extends IOException { 164ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private static final long serialVersionUID = 1L; 1651431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank 1661431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank EasParserException() { 1671431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank super("WBXML format error"); 1681431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank } 1691431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank 1703d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner EasParserException(final String reason) { 1711431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank super(reason); 1721431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank } 173ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 174ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 175ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public boolean parse() throws IOException, EasException { 176ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return false; 177ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 178ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1793d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner public Parser(final InputStream in) throws IOException { 18026d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank setInput(in, true); 18126d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 18226d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 18326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank /** 18426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank * Constructor for use when switching parsers within a input stream 18526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank * @param parser an existing, initialized parser 18626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank * @throws IOException 18726d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank */ 1883d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner public Parser(final Parser parser) throws IOException { 18926d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank setInput(parser.in, false); 190ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 191ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 192bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank protected InputStream getInput() { 193bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank return in; 194bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank } 195bb12673b0aa36ff0751ddcffe02223c6100f424eMarc Blank 196ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 197ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Turns on data capture; this is used to create test streams that represent "live" data and 198ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * can be used against the various parsers. 199ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 200ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public void captureOn() { 201ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank capture = true; 202ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank captureArray = new ArrayList<Integer>(); 203ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 204ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 205ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 206ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Turns off data capture; writes the captured data to a specified file. 207ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 2083d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner public void captureOff(final Context context, final String file) { 209ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 2103d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner final FileOutputStream out = context.openFileOutput(file, 2113d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner Context.MODE_WORLD_WRITEABLE); 212ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank out.write(captureArray.toString().getBytes()); 213ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank out.close(); 214ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } catch (FileNotFoundException e) { 215ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // This is debug code; exceptions aren't interesting. 216ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } catch (IOException e) { 217ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // This is debug code; exceptions aren't interesting. 218ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 219ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 220ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 221ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 2223d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * Return the value of the current tag, as a byte array. Throws EasParserException 2233d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * if neither opaque nor text data is present. Never returns null--returns 2243d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * an empty byte[] array for empty data. 22577186bb1a174432ef272584374942d8b9228e39cMarc Blank * 22677186bb1a174432ef272584374942d8b9228e39cMarc Blank * @return the byte array value of the current tag 22777186bb1a174432ef272584374942d8b9228e39cMarc Blank * @throws IOException 22877186bb1a174432ef272584374942d8b9228e39cMarc Blank */ 22977186bb1a174432ef272584374942d8b9228e39cMarc Blank public byte[] getValueBytes() throws IOException { 2303d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner final String name = startTag.toString(); 2313d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner 2323d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner getNext(); 2333d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner // This means there was no value given, just <Foo/>; we'll return empty array 2343d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner if (type == END) { 2353d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner log("No value for tag: " + name); 2363d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner return new byte[0]; 2373d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } else if (type != OPAQUE && type != TEXT) { 2383d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner throw new EasParserException("Expected OPAQUE or TEXT data for tag " + name); 2393d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } 2403d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner 2413d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner // Save the value 2423d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner final byte[] val = type == OPAQUE ? bytes : text.getBytes("UTF-8"); 2433d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner // Read the next token; it had better be the end of the current tag 2443d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner getNext(); 2453d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner // If not, throw an exception 2463d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner if (type != END) { 2473d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner throw new EasParserException("No END found for tag " + name); 2483d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } 2493d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner return val; 25077186bb1a174432ef272584374942d8b9228e39cMarc Blank } 25177186bb1a174432ef272584374942d8b9228e39cMarc Blank 25277186bb1a174432ef272584374942d8b9228e39cMarc Blank /** 2533d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * Return the value of the current tag, as a String. Throws EasParserException 2543d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * for non-text data. Never returns null--returns an empty string if no data. 255ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 256ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @return the String value of the current tag 257ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 258ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 259ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String getValue() throws IOException { 2603d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner final String name = startTag.toString(); 2613d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner 2623d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner getNext(); 263377230593dca7cb01483bfaf93959e5821f5f028Marc Blank // This means there was no value given, just <Foo/>; we'll return empty string for now 264377230593dca7cb01483bfaf93959e5821f5f028Marc Blank if (type == END) { 2653d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner log("No value for tag: " + name); 266377230593dca7cb01483bfaf93959e5821f5f028Marc Blank return ""; 2673d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } else if (type != TEXT) { 2683d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner throw new EasParserException("Expected TEXT data for tag " + name); 269377230593dca7cb01483bfaf93959e5821f5f028Marc Blank } 2703d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner 271ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Save the value 2723d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner final String val = text; 273ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Read the next token; it had better be the end of the current tag 2743d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner getNext(); 275ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If not, throw an exception 276ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (type != END) { 2773d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner throw new EasParserException("No END found for tag " + name); 278ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 279ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return val; 280ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 281ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 282ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 2833d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * Return the value of the current tag, as an integer. Throws EasParserException 2843d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * for non text data, and text data that doesn't parse as an integer. Returns 2853d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner * 0 for empty data. 286ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 287ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @return the integer value of the current tag 288ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 289ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 2903d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner public int getValueInt() throws IOException { 2913d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner final String val = getValue(); 2923d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner if (val.length() == 0) { 293377230593dca7cb01483bfaf93959e5821f5f028Marc Blank return 0; 294377230593dca7cb01483bfaf93959e5821f5f028Marc Blank } 2953d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner 2963d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner int num; 2973d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner try { 2983d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner num = Integer.parseInt(val); 2993d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } catch (NumberFormatException e) { 3003d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner throw new EasParserException("Tag " + startTag + ": " + e.getMessage()); 301ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 3023d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner return num; 303ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 304ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 305ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 306ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Return the next tag found in the stream; special tags END and END_DOCUMENT are used to 307ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * mark the end of the current tag and end of document. If we hit end of document without 308ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * looking for it, generate an EodException. The tag returned consists of the page number 309ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * shifted PAGE_SHIFT bits OR'd with the tag retrieved from the stream. Thus, all tags returned 310ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * are unique. 311ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 312ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @param endingTag the tag that would represent the end of the tag we're processing 313ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @return the next tag found 314ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 315ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 3163d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner public int nextTag(final int endingTag) throws IOException { 3173d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner while (getNext() != DONE) { 318ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If we're a start, set tag to include the page and return it 319ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (type == START) { 3203d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner tag = startTag.getTagNum(); 321ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return tag; 322ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If we're at the ending tag we're looking for, return the END signal 3233d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner } else if (type == END && startTag.getTagNum() == endingTag) { 324ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return END; 325ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 326ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 327ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // We're at end of document here. If we're looking for it, return END_DOCUMENT 3283d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner if (endingTag == START_DOCUMENT) { 329ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return END_DOCUMENT; 330ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 331ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Otherwise, we've prematurely hit end of document, so exception out 332ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // EodException is a subclass of IOException; this will be treated as an IO error by 333bb0141b49e7eff978fa445249dc888461ea581e3Martin Hibdon // EasService 334ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new EodException(); 335ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 336ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 337ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 338ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Skip anything found in the stream until the end of the current tag is reached. This can be 339ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * used to ignore stretches of xml that aren't needed by the parser. 340ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 341ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 342ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 343ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public void skipTag() throws IOException { 3443d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner final int thisTag = startTag.getTagNum(); 345ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Just loop until we hit the end of the current tag 3463d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner while (getNext() != DONE) { 3473d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner if (type == END && startTag.getTagNum() == thisTag) { 348ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return; 349ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 350ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 351ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 352ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If we're at end of document, that's bad 353ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new EofException(); 354ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 355ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 356ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 357ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Initializes the parser with an input stream; reads the first 4 bytes (which are always the 358ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * same in EAS, and then sets the tag table to point to page 0 (by definition, the starting 359ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * page). 360ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 361ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @param in the InputStream associated with this parser 362ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 363ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 3643d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner public void setInput(final InputStream in, final boolean initialize) throws IOException { 365ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank this.in = in; 3669f551ed0c2794a4ebb775df0dc5f714cf07f29b7Marc Blank if ((in != null) && initialize) { 367bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank // If we fail on the very first byte, report an empty stream 368bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank try { 3693d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner final int version = readByte(); // version 370bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } catch (EofException e) { 371bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank throw new EmptyStreamException(); 372bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } 3733d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner readInt(); // public identifier 37426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank readInt(); // 106 (UTF-8) 3753d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner final int stringTableLength = readInt(); // string table length 3763d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner if (stringTableLength != 0) { 3773d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner throw new EasParserException("WBXML string table unsupported"); 3783d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } 37926d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 380ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 381ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 38245fed27dbea77923779f6142c0341b3c80a77eddMarc Blank @VisibleForTesting 3833d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner void resetInput(final InputStream in) { 38402e3436296ad5a638a1b6f349555e39964c6d13dMarc Blank this.in = in; 38545fed27dbea77923779f6142c0341b3c80a77eddMarc Blank try { 38645fed27dbea77923779f6142c0341b3c80a77eddMarc Blank // Read leading zero 38745fed27dbea77923779f6142c0341b3c80a77eddMarc Blank read(); 38845fed27dbea77923779f6142c0341b3c80a77eddMarc Blank } catch (IOException e) { 38945fed27dbea77923779f6142c0341b3c80a77eddMarc Blank } 39002e3436296ad5a638a1b6f349555e39964c6d13dMarc Blank } 39126d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 3923d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner void log(final String str) { 3933c193c425ff383dbc94514d055ae6778821d8471Martin Hibdon if (!EasService.getProtocolLogging()) { 3943d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner return; 3953d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } 3963d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner final String logStr; 397f9423affa52661c2f552df35f0b9ddeecd8fa8feMarc Blank int cr = str.indexOf('\n'); 398f9423affa52661c2f552df35f0b9ddeecd8fa8feMarc Blank if (cr > 0) { 3993d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner logStr = str.substring(0, cr); 4003d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner } else { 4013d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner logStr = str; 402f9423affa52661c2f552df35f0b9ddeecd8fa8feMarc Blank } 4033d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner final char [] charArray = new char[startTagArray.size() * 2]; 4043d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner Arrays.fill(charArray, ' '); 4053d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner final String indent = new String(charArray); 4063c193c425ff383dbc94514d055ae6778821d8471Martin Hibdon LogUtils.d(LOG_TAG, "%s", indent + logStr); 4073c193c425ff383dbc94514d055ae6778821d8471Martin Hibdon if (EasService.getFileLogging()) { 4083d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner FileLogger.log(LOG_TAG, logStr); 409c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank } 410c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank } 411c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank 4123d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner void logVerbose(final String str) { 4133d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner if (LOG_VERBOSE) { 4143d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner log(str); 4153d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } 4163d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } 4173d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner 4183d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner protected void pushTag(final int id) { 4193d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner page = id >>> Tags.PAGE_SHIFT; 42026d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank push(id); 42126d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 42226d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 42340038a7f263c5463f8a0c08ffa21111b3e483694Martin Hibdon protected void pop() { 42426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank // Retrieve the now-current startTag from our stack 4255bad4cfbe9a6401d5b4cde5bb8b4eec8d5fab82fJay Shrauner startTag = startTagArray.removeFirst(); 4263d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner log("</" + startTag + '>'); 42726d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 42826d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 4293d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner private void push(final int id) { 4303d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner startTag = new Tag(page, id); 4313d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner noContent = startTag.mNoContent; 4323d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner log("<" + startTag + (noContent ? '/' : "") + '>'); 43326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank // Save the startTag to our stack 4343d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner startTagArray.addFirst(startTag); 43526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 43626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 437ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 438ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Return the next piece of data from the stream. The return value indicates the type of data 439ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * that has been retrieved - START (start of tag), END (end of tag), DONE (end of stream), or 440ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * TEXT (the value of a tag) 441ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 442ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @return the type of data retrieved 443ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 444ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 4453d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner private final int getNext() throws IOException { 4463d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner bytes = null; 4473d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner text = null; 4483d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner 449ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (noContent) { 4503d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner startTagArray.removeFirst(); 451ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank type = END; 452ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank noContent = false; 453ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return type; 454ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 455ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 4563d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner int id = read(); 457ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (id == Wbxml.SWITCH_PAGE) { 458ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Get the new page number 4593d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner page = readByte(); 460ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Retrieve the current tag table 4613d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner if (!Tags.isValidPage(page)) { 4623d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner // Unknown code page. These seem to happen mostly because of 4633d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner // invalid data from the server so throw an exception here. 4643d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner throw new EasParserException("Unknown code page " + page); 4653d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } 4663d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner logVerbose("Page: " + page); 4673d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner id = read(); 468ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 469ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 470ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (id) { 471ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank case EOF_BYTE: 472ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // End of document 473ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank type = DONE; 474ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 475ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 476ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank case Wbxml.END: 477ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank type = END; 47826d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank pop(); 479ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 480ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 481ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank case Wbxml.STR_I: 482ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Inline string 483ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank type = TEXT; 4843d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner text = readInlineString(); 4853d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner log(startTag + ": " + text); 486ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 487ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 48877186bb1a174432ef272584374942d8b9228e39cMarc Blank case Wbxml.OPAQUE: 48977186bb1a174432ef272584374942d8b9228e39cMarc Blank // Integer length + opaque data 4903d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner type = OPAQUE; 4913d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner final int length = readInt(); 49277186bb1a174432ef272584374942d8b9228e39cMarc Blank bytes = new byte[length]; 49377186bb1a174432ef272584374942d8b9228e39cMarc Blank for (int i = 0; i < length; i++) { 49477186bb1a174432ef272584374942d8b9228e39cMarc Blank bytes[i] = (byte)readByte(); 49577186bb1a174432ef272584374942d8b9228e39cMarc Blank } 4963d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner log(startTag + ": (opaque:" + length + ") "); 49777186bb1a174432ef272584374942d8b9228e39cMarc Blank break; 49877186bb1a174432ef272584374942d8b9228e39cMarc Blank 499ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 5003d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner if (Tags.isGlobalTag(id & Tags.PAGE_MASK)) { 5013d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner throw new EasParserException(String.format( 5023d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner "Unhandled WBXML global token 0x%02X", id)); 5033d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } 5043d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner if ((id & Wbxml.WITH_ATTRIBUTES) != 0) { 5053d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner throw new EasParserException(String.format( 5063d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner "Attributes unsupported, tag 0x%02X", id)); 5073d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner } 508ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank type = START; 50926d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank push(id); 510ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 511ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 512ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Return the type of data we're dealing with 513ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return type; 514ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 515ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 516ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 517ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Read an int from the input stream, and capture it if necessary for debugging. Seems a small 518ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * price to pay... 519ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 520ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @return the int read 521ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 522ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 523ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private int read() throws IOException { 524ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int i; 525ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank i = in.read(); 526ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (capture) { 527ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank captureArray.add(i); 528ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 5293d3a9d01938c0b25f629985473fcef8f7d4455eeJay Shrauner logVerbose("Byte: " + i); 530ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return i; 531ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 532ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 533ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private int readByte() throws IOException { 534ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int i = read(); 535ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (i == EOF_BYTE) { 536ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new EofException(); 537ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 538ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return i; 539ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 540ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 5413d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner /** 5423d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner * Throws EasParserException if detects integer encoded with more than 5 5433d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner * bytes. A uint_32 needs 5 bytes to fully encode 32 bits so if the high 5443d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner * bit is set for more than 4 bytes, something is wrong with the data 5453d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner * stream. 5463d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner */ 547ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private int readInt() throws IOException { 548ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int result = 0; 549ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int i; 5503d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner int numBytes = 0; 551ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 552ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank do { 5533d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner if (++numBytes > 5) { 5543d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner throw new EasParserException("Invalid integer encoding, too many bytes"); 5553d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner } 556ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank i = readByte(); 557ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank result = (result << 7) | (i & 0x7f); 558ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } while ((i & 0x80) != 0); 559ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 560ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return result; 561ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 562ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 563ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 564ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Read an inline string from the stream 565ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 566ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @return the String as parsed from the stream 567ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 568ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 569ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private String readInlineString() throws IOException { 5703d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(256); 571ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (true) { 5723d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner final int i = read(); 573ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (i == 0) { 574ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 575ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else if (i == EOF_BYTE) { 576ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new EofException(); 577ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 57861563f075e07b9961ede4000d815865edc0f2497Marc Blank outputStream.write(i); 579ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 58061563f075e07b9961ede4000d815865edc0f2497Marc Blank outputStream.flush(); 5813d52ff729b82a20802bb6d5a8952040f4d961cefJay Shrauner final String res = outputStream.toString("UTF-8"); 58261563f075e07b9961ede4000d815865edc0f2497Marc Blank outputStream.close(); 583ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return res; 584ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 585dabb41d364a54a60df0e9d3750cb37faed626743Elliott Hughes} 586