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; 24c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blankimport com.android.exchange.utility.FileLogger; 25942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedyimport com.android.mail.utils.LogUtils; 2645fed27dbea77923779f6142c0341b3c80a77eddMarc Blankimport com.google.common.annotations.VisibleForTesting; 27ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 2861563f075e07b9961ede4000d815865edc0f2497Marc Blankimport java.io.ByteArrayOutputStream; 297c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport java.io.FileNotFoundException; 307c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport java.io.FileOutputStream; 317c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport java.io.IOException; 327c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport java.io.InputStream; 337c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankimport java.util.ArrayList; 347c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank 35ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/** 36ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Extremely fast and lightweight WBXML parser, implementing only the subset of WBXML that 37ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * EAS uses (as defined in the EAS specification) 38ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 39ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 407c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankpublic abstract class Parser { 4134d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank private static final boolean LOG_VERBOSE = false; 4234d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank 43110837ebff288a75f9bda067c38e2c46797d99b5Alon Albert private static final String LOG_TAG = Eas.LOG_TAG; 44ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 45ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The following constants are Wbxml standard 46ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public static final int START_DOCUMENT = 0; 47ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public static final int DONE = 1; 48ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public static final int START = 2; 49ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public static final int END = 3; 50ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public static final int TEXT = 4; 51ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public static final int END_DOCUMENT = 3; 52ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private static final int NOT_FETCHED = Integer.MIN_VALUE; 53ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private static final int NOT_ENDED = Integer.MIN_VALUE; 54ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private static final int EOF_BYTE = -1; 5526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 56377230593dca7cb01483bfaf93959e5821f5f028Marc Blank // Where tags start in a page 57377230593dca7cb01483bfaf93959e5821f5f028Marc Blank private static final int TAG_BASE = 5; 58ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 59ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu private boolean logging = false; 60ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu private boolean capture = false; 61ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 62ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private ArrayList<Integer> captureArray; 63ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 64ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The input stream for this parser 65ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private InputStream in; 66ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 67ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The current tag depth 68ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private int depth; 69ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 70ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The upcoming (saved) id from the stream 71ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private int nextId = NOT_FETCHED; 72ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 73ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The current tag table (i.e. the tag table for the current page) 74ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private String[] tagTable; 75ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 76ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // An array of tag tables, as defined in EasTags 7777186bb1a174432ef272584374942d8b9228e39cMarc Blank static private String[][] tagTables = new String[Tags.pages.length + 1][]; 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 82ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The stack of tags being processed 83ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private int[] startTagArray = new int[32]; 84ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 85ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The following vars are available to all to avoid method calls that represent the state of 86ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // the parser at any given time 87ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public int endTag = NOT_ENDED; 88ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 89ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public int startTag; 90ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 91ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The type of the last token read 92ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public int type; 93ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 94ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The current page 95ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public int page; 96ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 97ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The current tag 98ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public int tag; 99ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 100ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The name of the current tag 101ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String name; 102ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 103ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Whether the current tag is associated with content (a value) 104ae073cce37583f350ee4f1df4847c9c26eadb404Marc Blank public boolean noContent; 105ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 106ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The value read, as a String. Only one of text or num will be valid, depending on whether the 107ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // value was requested as a String or an int (to avoid wasted effort in parsing) 108ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String text; 109ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 110ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The value read, as an int 111ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public int num; 112ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 11377186bb1a174432ef272584374942d8b9228e39cMarc Blank // The value read, as bytes 11477186bb1a174432ef272584374942d8b9228e39cMarc Blank public byte[] bytes; 11577186bb1a174432ef272584374942d8b9228e39cMarc Blank 116ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu // TODO: Define a new parse exception type rather than lumping these in as IOExceptions. 117ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu 118bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank /** 119bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank * Generated when the parser comes to EOF prematurely during parsing (i.e. in error) 120bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank */ 121ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public class EofException extends IOException { 122ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private static final long serialVersionUID = 1L; 123ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 124ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 125bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank /** 126bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank * An EmptyStreamException is an EofException that occurs reading the first byte in the parser's 127bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank * input stream; in other words, the stream had no content. 128bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank */ 129bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank public class EmptyStreamException extends EofException { 130bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank private static final long serialVersionUID = 1L; 131bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } 132bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank 133ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public class EodException extends IOException { 134ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private static final long serialVersionUID = 1L; 135ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 136ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 137ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public class EasParserException extends IOException { 138ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private static final long serialVersionUID = 1L; 1391431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank 1401431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank EasParserException() { 1411431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank super("WBXML format error"); 1421431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank } 1431431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank 1441431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank EasParserException(String reason) { 1451431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank super(reason); 1461431215b5fc40d0d6498b0fe602ad4d1b8a66ff3Marc Blank } 147ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 148ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 149ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public boolean parse() throws IOException, EasException { 150ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return false; 151ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 152ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 153ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 154ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Initialize the tag tables; they are constant 155ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 156ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 157ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank { 1587c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank String[][] pages = Tags.pages; 159ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank for (int i = 0; i < pages.length; i++) { 160ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String[] page = pages[i]; 161ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (page.length > 0) { 162ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank tagTables[i] = page; 163ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 164ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 165ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 166ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1677c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank public Parser(InputStream in) throws IOException { 16826d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank setInput(in, true); 16926d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank logging = Eas.PARSER_LOG; 17026d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 17126d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 17226d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank /** 17326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank * Constructor for use when switching parsers within a input stream 17426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank * @param parser an existing, initialized parser 17526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank * @throws IOException 17626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank */ 17726d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank public Parser(Parser parser) throws IOException { 17826d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank setInput(parser.in, false); 1797c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank logging = Eas.PARSER_LOG; 180ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 181ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 182ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 183942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy * Set the debug state of the parser. When debugging is on, every token is logged (LogUtils.v) 184942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy * to the console. 185ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 186ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @param val the desired state for debug output 187ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 188ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public void setDebug(boolean val) { 1897c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank logging = val; 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 */ 208ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public void captureOff(Context context, String file) { 209ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 210ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank FileOutputStream out = context.openFileOutput(file, Context.MODE_WORLD_WRITEABLE); 211ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank out.write(captureArray.toString().getBytes()); 212ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank out.close(); 213ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } catch (FileNotFoundException e) { 214ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // This is debug code; exceptions aren't interesting. 215ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } catch (IOException e) { 216ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // This is debug code; exceptions aren't interesting. 217ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 218ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 219ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 220ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 22177186bb1a174432ef272584374942d8b9228e39cMarc Blank * Return the value of the current tag, as a byte array. Note that the result of this call 22277186bb1a174432ef272584374942d8b9228e39cMarc Blank * is indeterminate, and possibly null, if the value of the tag is not a byte array 22377186bb1a174432ef272584374942d8b9228e39cMarc Blank * 22477186bb1a174432ef272584374942d8b9228e39cMarc Blank * @return the byte array value of the current tag 22577186bb1a174432ef272584374942d8b9228e39cMarc Blank * @throws IOException 22677186bb1a174432ef272584374942d8b9228e39cMarc Blank */ 22777186bb1a174432ef272584374942d8b9228e39cMarc Blank public byte[] getValueBytes() throws IOException { 22877186bb1a174432ef272584374942d8b9228e39cMarc Blank getValue(); 22977186bb1a174432ef272584374942d8b9228e39cMarc Blank return bytes; 23077186bb1a174432ef272584374942d8b9228e39cMarc Blank } 23177186bb1a174432ef272584374942d8b9228e39cMarc Blank 23277186bb1a174432ef272584374942d8b9228e39cMarc Blank /** 23377186bb1a174432ef272584374942d8b9228e39cMarc Blank * Return the value of the current tag, as a String. Note that the result of this call is 23477186bb1a174432ef272584374942d8b9228e39cMarc Blank * indeterminate, and possibly null, if the value of the tag is not an immediate string 235ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 236ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @return the String value of the current tag 237ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 238ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 239ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String getValue() throws IOException { 240ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The false argument tells getNext to return the value as a String 241ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank getNext(false); 242377230593dca7cb01483bfaf93959e5821f5f028Marc Blank // This means there was no value given, just <Foo/>; we'll return empty string for now 243377230593dca7cb01483bfaf93959e5821f5f028Marc Blank if (type == END) { 244377230593dca7cb01483bfaf93959e5821f5f028Marc Blank if (logging) { 245377230593dca7cb01483bfaf93959e5821f5f028Marc Blank log("No value for tag: " + tagTable[startTag - TAG_BASE]); 246377230593dca7cb01483bfaf93959e5821f5f028Marc Blank } 247377230593dca7cb01483bfaf93959e5821f5f028Marc Blank return ""; 248377230593dca7cb01483bfaf93959e5821f5f028Marc Blank } 249ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Save the value 250ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String val = text; 251ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Read the next token; it had better be the end of the current tag 252ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank getNext(false); 253ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If not, throw an exception 254ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (type != END) { 255ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new IOException("No END found!"); 256ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 257ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return val; 258ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 259ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 260ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 26177186bb1a174432ef272584374942d8b9228e39cMarc Blank * Return the value of the current tag, as an integer. Note that the value of this call is 26277186bb1a174432ef272584374942d8b9228e39cMarc Blank * indeterminate if the value of this tag is not an immediate string parsed as an integer 263ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 264ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @return the integer value of the current tag 265ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 266ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 267ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public int getValueInt() throws IOException { 268ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // The true argument to getNext indicates the desire for an integer return value 269ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank getNext(true); 270377230593dca7cb01483bfaf93959e5821f5f028Marc Blank if (type == END) { 271377230593dca7cb01483bfaf93959e5821f5f028Marc Blank return 0; 272377230593dca7cb01483bfaf93959e5821f5f028Marc Blank } 273ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Save the value 274ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int val = num; 275ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Read the next token; it had better be the end of the current tag 276ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank getNext(false); 277ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If not, throw an exception 278ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (type != END) { 279ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new IOException("No END found!"); 280ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 281ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return val; 282ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 283ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 284ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 285ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Return the next tag found in the stream; special tags END and END_DOCUMENT are used to 286ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * mark the end of the current tag and end of document. If we hit end of document without 287ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * looking for it, generate an EodException. The tag returned consists of the page number 288ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * shifted PAGE_SHIFT bits OR'd with the tag retrieved from the stream. Thus, all tags returned 289ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * are unique. 290ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 291ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @param endingTag the tag that would represent the end of the tag we're processing 292ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @return the next tag found 293ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 294ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 295ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public int nextTag(int endingTag) throws IOException { 296ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Lose the page information 2977c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank endTag = endingTag &= Tags.PAGE_MASK; 298ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (getNext(false) != DONE) { 299ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If we're a start, set tag to include the page and return it 300ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (type == START) { 301ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank tag = page | startTag; 302ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return tag; 303ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If we're at the ending tag we're looking for, return the END signal 304ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else if (type == END && startTag == endTag) { 305ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return END; 306ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 307ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 308ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // We're at end of document here. If we're looking for it, return END_DOCUMENT 309ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (endTag == START_DOCUMENT) { 310ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return END_DOCUMENT; 311ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 312ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Otherwise, we've prematurely hit end of document, so exception out 313ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // EodException is a subclass of IOException; this will be treated as an IO error by 314385a0be662509754e687bcfa9813208b050bf951Marc Blank // ExchangeService 315ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new EodException(); 316ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 317ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 318ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 319ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Skip anything found in the stream until the end of the current tag is reached. This can be 320ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * used to ignore stretches of xml that aren't needed by the parser. 321ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 322ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 323ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 324ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public void skipTag() throws IOException { 325ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int thisTag = startTag; 326ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Just loop until we hit the end of the current tag 327ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (getNext(false) != DONE) { 328ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (type == END && startTag == thisTag) { 329ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return; 330ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 331ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 332ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 333ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If we're at end of document, that's bad 334ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new EofException(); 335ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 336ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 337ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 338ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Retrieve the next token from the input stream 339ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 340ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @return the token found 341ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 342ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 343ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public int nextToken() throws IOException { 344ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank getNext(false); 345ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return type; 346ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 347ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 348ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 349ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Initializes the parser with an input stream; reads the first 4 bytes (which are always the 350ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * same in EAS, and then sets the tag table to point to page 0 (by definition, the starting 351ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * page). 352ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 353ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @param in the InputStream associated with this parser 354ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 355ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 35626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank public void setInput(InputStream in, boolean initialize) throws IOException { 357ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank this.in = in; 3589f551ed0c2794a4ebb775df0dc5f714cf07f29b7Marc Blank if ((in != null) && initialize) { 359bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank // If we fail on the very first byte, report an empty stream 360bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank try { 361bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank readByte(); // version 362bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } catch (EofException e) { 363bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank throw new EmptyStreamException(); 364bc2c2bd71a2026ac4bb54e6bf82f02df585f8a87Marc Blank } 36526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank readInt(); // ? 36626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank readInt(); // 106 (UTF-8) 36726d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank readInt(); // string table length 36826d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 369ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank tagTable = tagTables[0]; 370ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 371ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 37245fed27dbea77923779f6142c0341b3c80a77eddMarc Blank @VisibleForTesting 37345fed27dbea77923779f6142c0341b3c80a77eddMarc Blank void resetInput(InputStream in) { 37402e3436296ad5a638a1b6f349555e39964c6d13dMarc Blank this.in = in; 37545fed27dbea77923779f6142c0341b3c80a77eddMarc Blank try { 37645fed27dbea77923779f6142c0341b3c80a77eddMarc Blank // Read leading zero 37745fed27dbea77923779f6142c0341b3c80a77eddMarc Blank read(); 37845fed27dbea77923779f6142c0341b3c80a77eddMarc Blank } catch (IOException e) { 37945fed27dbea77923779f6142c0341b3c80a77eddMarc Blank } 38002e3436296ad5a638a1b6f349555e39964c6d13dMarc Blank } 38126d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 382c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank void log(String str) { 383f9423affa52661c2f552df35f0b9ddeecd8fa8feMarc Blank int cr = str.indexOf('\n'); 384f9423affa52661c2f552df35f0b9ddeecd8fa8feMarc Blank if (cr > 0) { 385f9423affa52661c2f552df35f0b9ddeecd8fa8feMarc Blank str = str.substring(0, cr); 386f9423affa52661c2f552df35f0b9ddeecd8fa8feMarc Blank } 387ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu LogUtils.v(LOG_TAG, str); 388c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank if (Eas.FILE_LOG) { 389ff7e02603bc8196f411c0c491d74a42e747b7dc5Yu Ping Hu FileLogger.log(LOG_TAG, str); 390c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank } 391c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank } 392c1e79c036cd2a40e8a6e66b8ea4d37d121d355baMarc Blank 39326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank protected void pushTag(int id) { 39426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank page = id >> Tags.PAGE_SHIFT; 39526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank tagTable = tagTables[page]; 39626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank push(id); 39726d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 39826d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 39926d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank private void pop() { 40026d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank if (logging) { 40126d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank name = nameArray[depth]; 40234d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank log("</" + name + '>'); 40326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 40426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank // Retrieve the now-current startTag from our stack 40526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank startTag = endTag = startTagArray[depth]; 40626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank depth--; 40726d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 40826d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 40926d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank private void push(int id) { 41026d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank // The tag is in the low 6 bits 41126d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank startTag = id & 0x3F; 41226d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank // If the high bit is set, there is content (a value) to be read 41326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank noContent = (id & 0x40) == 0; 41426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank depth++; 41526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank if (logging) { 41626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank name = tagTable[startTag - TAG_BASE]; 41726d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank nameArray[depth] = name; 41834d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank log("<" + name + (noContent ? '/' : "") + '>'); 41926d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 42026d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank // Save the startTag to our stack 42126d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank startTagArray[depth] = startTag; 42226d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 42326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 424ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 425ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Return the next piece of data from the stream. The return value indicates the type of data 426ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * that has been retrieved - START (start of tag), END (end of tag), DONE (end of stream), or 427ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * TEXT (the value of a tag) 428ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 429ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @param asInt whether a TEXT value should be parsed as a String or an int. 430ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @return the type of data retrieved 431ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 432ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 433ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private final int getNext(boolean asInt) throws IOException { 434ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (noContent) { 43536c35287567e6f137b80f035b5fbf4d745541860Marc Blank nameArray[depth--] = null; 436ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank type = END; 437ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank noContent = false; 438ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return type; 439ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 440ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 441ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank text = null; 442ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank name = null; 443ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 444ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int id = nextId (); 445ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (id == Wbxml.SWITCH_PAGE) { 446ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank nextId = NOT_FETCHED; 447ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Get the new page number 448ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int pg = readByte(); 449ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Save the shifted page to add into the startTag in nextTag 4507c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank page = pg << Tags.PAGE_SHIFT; 45134d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank if (LOG_VERBOSE) { 45234d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank log("Page: " + page); 45334d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } 454ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Retrieve the current tag table 455ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank tagTable = tagTables[pg]; 456ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank id = nextId(); 457ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 458ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank nextId = NOT_FETCHED; 459ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 460ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (id) { 461ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank case EOF_BYTE: 462ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // End of document 463ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank type = DONE; 464ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 465ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 466ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank case Wbxml.END: 467ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank type = END; 46826d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank pop(); 469ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 470ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 471ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank case Wbxml.STR_I: 472ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Inline string 473ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank type = TEXT; 474ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (asInt) { 475ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank num = readInlineInt(); 476ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else { 477ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank text = readInlineString(); 478ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 4797c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank if (logging) { 480377230593dca7cb01483bfaf93959e5821f5f028Marc Blank name = tagTable[startTag - TAG_BASE]; 481f9423affa52661c2f552df35f0b9ddeecd8fa8feMarc Blank log(name + ": " + (asInt ? Integer.toString(num) : text)); 482ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 483ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 484ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 48577186bb1a174432ef272584374942d8b9228e39cMarc Blank case Wbxml.OPAQUE: 48677186bb1a174432ef272584374942d8b9228e39cMarc Blank // Integer length + opaque data 48777186bb1a174432ef272584374942d8b9228e39cMarc Blank int length = readInt(); 48877186bb1a174432ef272584374942d8b9228e39cMarc Blank bytes = new byte[length]; 48977186bb1a174432ef272584374942d8b9228e39cMarc Blank for (int i = 0; i < length; i++) { 49077186bb1a174432ef272584374942d8b9228e39cMarc Blank bytes[i] = (byte)readByte(); 49177186bb1a174432ef272584374942d8b9228e39cMarc Blank } 492c171a2362e6db78385463e3b7b1bc66585fdcdfcMarc Blank if (logging) { 493c171a2362e6db78385463e3b7b1bc66585fdcdfcMarc Blank name = tagTable[startTag - TAG_BASE]; 494c171a2362e6db78385463e3b7b1bc66585fdcdfcMarc Blank log(name + ": (opaque:" + length + ") "); 495c171a2362e6db78385463e3b7b1bc66585fdcdfcMarc Blank } 49677186bb1a174432ef272584374942d8b9228e39cMarc Blank break; 49777186bb1a174432ef272584374942d8b9228e39cMarc Blank 498ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 499ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank type = START; 50026d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank push(id); 501ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 502ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 503ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Return the type of data we're dealing with 504ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return type; 505ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 506ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 507ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 508ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Read an int from the input stream, and capture it if necessary for debugging. Seems a small 509ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * price to pay... 510ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 511ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @return the int read 512ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 513ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 514ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private int read() throws IOException { 515ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int i; 516ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank i = in.read(); 517ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (capture) { 518ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank captureArray.add(i); 519ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 52034d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank if (LOG_VERBOSE) { 52134d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank log("Byte: " + i); 52234d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } 523ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return i; 524ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 525ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 526ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private int nextId() throws IOException { 527ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (nextId == NOT_FETCHED) { 528ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank nextId = read(); 529ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 530ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return nextId; 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 541ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 542ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Read an integer from the stream; this is called when the parser knows that what follows is 543ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * an inline string representing an integer (e.g. the Read tag in Email has a value known to 544ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * be either "0" or "1") 545ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 546ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @return the integer as parsed from the stream 547ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 548ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 549ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private int readInlineInt() throws IOException { 550ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int result = 0; 551ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 552ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (true) { 553ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int i = readByte(); 554ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Inline strings are always terminated with a zero byte 555ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (i == 0) { 556ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return result; 557ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 558ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (i >= '0' && i <= '9') { 559ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank result = (result * 10) + (i - '0'); 560ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else { 561ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new IOException("Non integer"); 562ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 563ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 564ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 565ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 566ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private int readInt() throws IOException { 567ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int result = 0; 568ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int i; 569ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 570ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank do { 571ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank i = readByte(); 572ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank result = (result << 7) | (i & 0x7f); 573ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } while ((i & 0x80) != 0); 574ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 575ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return result; 576ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 577ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 578ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank /** 579ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Read an inline string from the stream 580ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 581ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @return the String as parsed from the stream 582ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * @throws IOException 583ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 584ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private String readInlineString() throws IOException { 58561563f075e07b9961ede4000d815865edc0f2497Marc Blank ByteArrayOutputStream outputStream = new ByteArrayOutputStream(256); 586ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (true) { 587ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank int i = read(); 588ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (i == 0) { 589ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 590ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else if (i == EOF_BYTE) { 591ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank throw new EofException(); 592ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 59361563f075e07b9961ede4000d815865edc0f2497Marc Blank outputStream.write(i); 594ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 59561563f075e07b9961ede4000d815865edc0f2497Marc Blank outputStream.flush(); 59661563f075e07b9961ede4000d815865edc0f2497Marc Blank String res = outputStream.toString("UTF-8"); 59761563f075e07b9961ede4000d815865edc0f2497Marc Blank outputStream.close(); 598ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return res; 599ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 600dabb41d364a54a60df0e9d3750cb37faed626743Elliott Hughes} 601