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