1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/* Copyright (c) 2002,2003, Stefan Haustein, Oberhausen, Rhld., Germany
2adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
3adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Permission is hereby granted, free of charge, to any person obtaining a copy
4adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * of this software and associated documentation files (the "Software"), to deal
5adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * in the Software without restriction, including without limitation the rights
6adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * to use, copy, modify, merge, publish, distribute, sublicense, and/or
7adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * sell copies of the Software, and to permit persons to whom the Software is
8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * furnished to do so, subject to the following conditions:
9adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * The  above copyright notice and this permission notice shall be included in
11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * all copies or substantial portions of the Software.
12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project *
13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * IN THE SOFTWARE. */
20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project// Contributors: Paul Hackenberger (unterminated entity handling in relaxed mode)
22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage org.kxml2.io;
24adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
25086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilsonimport java.io.Closeable;
26ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilsonimport java.io.IOException;
27ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilsonimport java.io.InputStream;
28ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilsonimport java.io.InputStreamReader;
29ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilsonimport java.io.Reader;
30ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilsonimport java.util.HashMap;
31ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilsonimport java.util.Map;
32a78c2aac2a73f001aa00971adfae90af4d6726fbJesse Wilsonimport libcore.internal.StringPool;
33ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilsonimport org.xmlpull.v1.XmlPullParser;
34ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilsonimport org.xmlpull.v1.XmlPullParserException;
35ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson
36ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson/**
3776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson * An XML pull parser with limited support for parsing internal DTDs.
38ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson */
39086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilsonpublic class KXmlParser implements XmlPullParser, Closeable {
40adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
416aa068b481cc4cca7765ce90fdf32f3eb2b5a77cElliott Hughes    private static final String PROPERTY_XMLDECL_VERSION
4276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            = "http://xmlpull.org/v1/doc/properties.html#xmldecl-version";
436aa068b481cc4cca7765ce90fdf32f3eb2b5a77cElliott Hughes    private static final String PROPERTY_XMLDECL_STANDALONE
4476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            = "http://xmlpull.org/v1/doc/properties.html#xmldecl-standalone";
456aa068b481cc4cca7765ce90fdf32f3eb2b5a77cElliott Hughes    private static final String PROPERTY_LOCATION = "http://xmlpull.org/v1/doc/properties.html#location";
4676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    private static final String FEATURE_RELAXED = "http://xmlpull.org/v1/doc/features.html#relaxed";
4776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
4876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    private static final Map<String, String> DEFAULT_ENTITIES = new HashMap<String, String>();
4976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    static {
5076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        DEFAULT_ENTITIES.put("lt", "<");
5176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        DEFAULT_ENTITIES.put("gt", ">");
5276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        DEFAULT_ENTITIES.put("amp", "&");
5376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        DEFAULT_ENTITIES.put("apos", "'");
5476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        DEFAULT_ENTITIES.put("quot", "\"");
5576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    }
5676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
577fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final int ELEMENTDECL = 11;
587fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final int ENTITYDECL = 12;
597fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final int ATTLISTDECL = 13;
607fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final int NOTATIONDECL = 14;
617fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final int PARAMETER_ENTITY_REF = 15;
62fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private static final char[] START_COMMENT = { '<', '!', '-', '-' };
63fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private static final char[] END_COMMENT = { '-', '-', '>' };
64bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson    private static final char[] COMMENT_DOUBLE_DASH = { '-', '-' };
65fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private static final char[] START_CDATA = { '<', '!', '[', 'C', 'D', 'A', 'T', 'A', '[' };
66fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private static final char[] END_CDATA = { ']', ']', '>' };
67fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private static final char[] START_PROCESSING_INSTRUCTION = { '<', '?' };
68fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private static final char[] END_PROCESSING_INSTRUCTION = { '?', '>' };
69fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private static final char[] START_DOCTYPE = { '<', '!', 'D', 'O', 'C', 'T', 'Y', 'P', 'E' };
707fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final char[] SYSTEM = { 'S', 'Y', 'S', 'T', 'E', 'M' };
717fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final char[] PUBLIC = { 'P', 'U', 'B', 'L', 'I', 'C' };
727fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final char[] START_ELEMENT = { '<', '!', 'E', 'L', 'E', 'M', 'E', 'N', 'T' };
737fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final char[] START_ATTLIST = { '<', '!', 'A', 'T', 'T', 'L', 'I', 'S', 'T' };
747fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final char[] START_ENTITY = { '<', '!', 'E', 'N', 'T', 'I', 'T', 'Y' };
757fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final char[] START_NOTATION = { '<', '!', 'N', 'O', 'T', 'A', 'T', 'I', 'O', 'N' };
767fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final char[] EMPTY = new char[] { 'E', 'M', 'P', 'T', 'Y' };
777fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final char[] ANY = new char[]{ 'A', 'N', 'Y' };
787fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final char[] NDATA = new char[]{ 'N', 'D', 'A', 'T', 'A' };
797fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final char[] NOTATION = new char[]{ 'N', 'O', 'T', 'A', 'T', 'I', 'O', 'N' };
807fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final char[] REQUIRED = new char[] { 'R', 'E', 'Q', 'U', 'I', 'R', 'E', 'D' };
817fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final char[] IMPLIED = new char[] { 'I', 'M', 'P', 'L', 'I', 'E', 'D' };
827fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private static final char[] FIXED = new char[] { 'F', 'I', 'X', 'E', 'D' };
83fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
84d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes    static final private String UNEXPECTED_EOF = "Unexpected EOF";
85adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    static final private String ILLEGAL_TYPE = "Wrong event type";
86fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    static final private int XML_DECLARATION = 998;
87adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
88adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    // general
89fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private String location;
90adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
91adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private String version;
92adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private Boolean standalone;
93086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    private String rootElementName;
94086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    private String systemId;
95086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    private String publicId;
96adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
9776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    /**
9876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * True if the {@code <!DOCTYPE>} contents are handled. The DTD defines
9976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * entity values and default attribute values. These values are parsed at
10076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * inclusion time and may contain both tags and entity references.
10176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     *
10276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * <p>If this is false, the user must {@link #defineEntityReplacementText
10376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * define entity values manually}. Such entity values are literal strings
10476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * and will not be parsed. There is no API to define default attributes
10576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * manually.
10676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     */
10776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    private boolean processDocDecl;
108adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private boolean processNsp;
109adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private boolean relaxed;
110ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson    private boolean keepNamespaceAttributes;
11176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
11276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    /**
11338e84b835c2101206d846f7ab6fc444914661753Jesse Wilson     * If non-null, the contents of the read buffer must be copied into this
11438e84b835c2101206d846f7ab6fc444914661753Jesse Wilson     * string builder before the read buffer is overwritten. This is used to
11538e84b835c2101206d846f7ab6fc444914661753Jesse Wilson     * capture the raw DTD text while parsing the DTD.
11638e84b835c2101206d846f7ab6fc444914661753Jesse Wilson     */
11738e84b835c2101206d846f7ab6fc444914661753Jesse Wilson    private StringBuilder bufferCapture;
11838e84b835c2101206d846f7ab6fc444914661753Jesse Wilson
11938e84b835c2101206d846f7ab6fc444914661753Jesse Wilson    /**
12076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * Entities defined in or for this document. This map is created lazily.
12176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     */
12276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    private Map<String, char[]> documentEntities;
12376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
12476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    /**
12576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * Default attributes in this document. The outer map's key is the element
12676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * name; the inner map's key is the attribute name. Both keys should be
12776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * without namespace adjustments. This map is created lazily.
12876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     */
12976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    private Map<String, Map<String, String>> defaultAttributes;
13076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
13176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
132adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private int depth;
133adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private String[] elementStack = new String[16];
134adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private String[] nspStack = new String[8];
135adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private int[] nspCounts = new int[4];
136adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
137adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    // source
138adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
139adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private Reader reader;
140adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private String encoding;
14176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    private ContentSource nextContentSource;
14276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    private char[] buffer = new char[8192];
143fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private int position = 0;
144fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private int limit = 0;
145fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
146fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    /*
147fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * Track the number of newlines and columns preceding the current buffer. To
148fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * compute the line and column of a position in the buffer, compute the line
149fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * and column in the buffer and add the preceding values.
150fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     */
151fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private int bufferStartLine;
152fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private int bufferStartColumn;
153adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
154fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    // the current token
155adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
156adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private int type;
157adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private boolean isWhitespace;
158adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private String namespace;
159adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private String prefix;
160adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private String name;
161fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private String text;
162adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
163adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private boolean degenerated;
164adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private int attributeCount;
1651ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson
1660424d12f9b8a19c4a451282bc8ae974f6109424dNarayan Kamath    // true iff. we've encountered the START_TAG of an XML element at depth == 0;
1670424d12f9b8a19c4a451282bc8ae974f6109424dNarayan Kamath    private boolean parsedTopLevelStartTag;
1680424d12f9b8a19c4a451282bc8ae974f6109424dNarayan Kamath
169fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    /*
1701ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson     * The current element's attributes arranged in groups of 4:
1711ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson     * i + 0 = attribute namespace URI
1721ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson     * i + 1 = attribute namespace prefix
1731ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson     * i + 2 = attribute qualified name (may contain ":", as in "html:h1")
1741ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson     * i + 3 = attribute value
1751ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson     */
176adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private String[] attributes = new String[16];
177ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson
178adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private String error;
179adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
180adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    private boolean unresolved;
181adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
182a78c2aac2a73f001aa00971adfae90af4d6726fbJesse Wilson    public final StringPool stringPool = new StringPool();
183a78c2aac2a73f001aa00971adfae90af4d6726fbJesse Wilson
1841ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson    /**
185ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson     * Retains namespace attributes like {@code xmlns="http://foo"} or {@code xmlns:foo="http:foo"}
186ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson     * in pulled elements. Most applications will only be interested in the effective namespaces of
187ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson     * their elements, so these attributes aren't useful. But for structure preserving wrappers like
188ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson     * DOM, it is necessary to keep the namespace data around.
1891ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson     */
1901ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson    public void keepNamespaceAttributes() {
1911ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson        this.keepNamespaceAttributes = true;
1921ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson    }
1931ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson
194ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson    private boolean adjustNsp() throws XmlPullParserException {
195adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        boolean any = false;
196adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
197adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        for (int i = 0; i < attributeCount << 2; i += 4) {
198adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String attrName = attributes[i + 2];
199adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            int cut = attrName.indexOf(':');
200adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String prefix;
201adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
202adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (cut != -1) {
203adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                prefix = attrName.substring(0, cut);
204adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                attrName = attrName.substring(cut + 1);
205ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            } else if (attrName.equals("xmlns")) {
206adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                prefix = attrName;
207adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                attrName = null;
208ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            } else {
209adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                continue;
210ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            }
211adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
212adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (!prefix.equals("xmlns")) {
213adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                any = true;
214ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            } else {
215adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                int j = (nspCounts[depth]++) << 1;
216adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
217adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                nspStack = ensureCapacity(nspStack, j + 2);
218adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                nspStack[j] = attrName;
219adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                nspStack[j + 1] = attributes[i + 3];
220adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
221ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                if (attrName != null && attributes[i + 3].isEmpty()) {
222fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    checkRelaxed("illegal empty namespace");
223ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                }
224adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2251ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson                if (keepNamespaceAttributes) {
226f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes                    // explicitly set the namespace for unprefixed attributes
2271ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson                    // such as xmlns="http://foo"
2281ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson                    attributes[i] = "http://www.w3.org/2000/xmlns/";
2291ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson                    any = true;
2301ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson                } else {
2311ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson                    System.arraycopy(
2321ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson                            attributes,
2331ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson                            i + 4,
2341ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson                            attributes,
2351ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson                            i,
2361ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson                            ((--attributeCount) << 2) - i);
2371ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson
2381ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson                    i -= 4;
2391ec94feeb09591c30996c7c0834d6f131e204922Jesse Wilson                }
240adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
241adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
242adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
243adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (any) {
244adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            for (int i = (attributeCount << 2) - 4; i >= 0; i -= 4) {
245adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
246adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                String attrName = attributes[i + 2];
247adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                int cut = attrName.indexOf(':');
248adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
249ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                if (cut == 0 && !relaxed) {
250adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    throw new RuntimeException(
251ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                            "illegal attribute name: " + attrName + " at " + this);
252ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                } else if (cut != -1) {
253adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    String attrPrefix = attrName.substring(0, cut);
254adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
255adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    attrName = attrName.substring(cut + 1);
256adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
257adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    String attrNs = getNamespace(attrPrefix);
258adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
259ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                    if (attrNs == null && !relaxed) {
260adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        throw new RuntimeException(
261ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                                "Undefined Prefix: " + attrPrefix + " in " + this);
262ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                    }
263adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
264adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    attributes[i] = attrNs;
265adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    attributes[i + 1] = attrPrefix;
266adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    attributes[i + 2] = attrName;
267adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
268adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
269adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
270adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
271adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        int cut = name.indexOf(':');
272adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
273ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (cut == 0) {
274fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            checkRelaxed("illegal tag name: " + name);
275ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
276adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
277adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (cut != -1) {
278adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            prefix = name.substring(0, cut);
279adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            name = name.substring(cut + 1);
280adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
281adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
282adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        this.namespace = getNamespace(prefix);
283adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
284adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (this.namespace == null) {
285ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            if (prefix != null) {
286fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                checkRelaxed("undefined prefix: " + prefix);
287ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            }
288adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            this.namespace = NO_NAMESPACE;
289adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
290adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
291adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return any;
292adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
293adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
294ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson    private String[] ensureCapacity(String[] arr, int required) {
295ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (arr.length >= required) {
296adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return arr;
297ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
298adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String[] bigger = new String[required + 16];
299adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        System.arraycopy(arr, 0, bigger, 0, arr.length);
300adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return bigger;
301adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
302adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
303fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private void checkRelaxed(String errorMessage) throws XmlPullParserException {
304fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (!relaxed) {
305fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            throw new XmlPullParserException(errorMessage, this, null);
306fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
307fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (error == null) {
308fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            error = "Error: " + errorMessage;
309ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
310adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
311adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
312bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson    public int next() throws XmlPullParserException, IOException {
313bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        return next(false);
314bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson    }
315bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson
316bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson    public int nextToken() throws XmlPullParserException, IOException {
317bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        return next(true);
318bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson    }
319bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson
320bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson    private int next(boolean justOneToken) throws IOException, XmlPullParserException {
321ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (reader == null) {
322fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            throw new XmlPullParserException("setInput() must be called first.", this, null);
323ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
324adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
325ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (type == END_TAG) {
326adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            depth--;
327ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
328adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
329bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        // degenerated needs to be handled before error because of possible
330bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        // processor expectations(!)
331adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
332bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        if (degenerated) {
333bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            degenerated = false;
334bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            type = END_TAG;
335bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            return type;
336bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        }
337adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
338bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        if (error != null) {
339bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            if (justOneToken) {
340fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                text = error;
341adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                type = COMMENT;
342bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                error = null;
343bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                return type;
344bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            } else {
345bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                error = null;
346adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
347bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        }
348adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
3497fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        type = peekType(false);
350adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
351bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        if (type == XML_DECLARATION) {
352bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            readXmlDeclaration();
3537fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            type = peekType(false);
354bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        }
355bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson
356bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        text = null;
357bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        isWhitespace = true;
358bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        prefix = null;
359bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        name = null;
360bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        namespace = null;
361bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        attributeCount = -1;
362086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        boolean throwOnResolveFailure = !justOneToken;
363adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
364bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        while (true) {
365adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            switch (type) {
366adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
367bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            /*
368bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson             * Return immediately after encountering a start tag, end tag, or
369bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson             * the end of the document.
370bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson             */
371bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            case START_TAG:
372086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson                parseStartTag(false, throwOnResolveFailure);
373bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                return type;
374bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            case END_TAG:
375bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                readEndTag();
376bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                return type;
377bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            case END_DOCUMENT:
378bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                return type;
379bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson
380bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            /*
381bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson             * Return after any text token when we're looking for a single
382bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson             * token. Otherwise concatenate all text between tags.
383bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson             */
384fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            case ENTITY_REF:
385bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                if (justOneToken) {
386fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    StringBuilder entityTextBuilder = new StringBuilder();
387086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson                    readEntity(entityTextBuilder, true, throwOnResolveFailure, ValueContext.TEXT);
388fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    text = entityTextBuilder.toString();
389bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                    break;
390fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
391fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                // fall-through
392fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            case TEXT:
393086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson                text = readValue('<', !justOneToken, throwOnResolveFailure, ValueContext.TEXT);
394fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                if (depth == 0 && isWhitespace) {
395fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    type = IGNORABLE_WHITESPACE;
396fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
397bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                break;
398bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            case CDSECT:
399bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                read(START_CDATA);
400bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                text = readUntil(END_CDATA, true);
401bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                break;
402adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
403bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            /*
404bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson             * Comments, processing instructions and declarations are returned
405bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson             * when we're looking for a single token. Otherwise they're skipped.
406bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson             */
407bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            case COMMENT:
408bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                String commentText = readComment(justOneToken);
409bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                if (justOneToken) {
410bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                    text = commentText;
411bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                }
412bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                break;
413fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            case PROCESSING_INSTRUCTION:
414fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                read(START_PROCESSING_INSTRUCTION);
415bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                String processingInstruction = readUntil(END_PROCESSING_INSTRUCTION, justOneToken);
416bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                if (justOneToken) {
417bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                    text = processingInstruction;
418adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
419bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                break;
420fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            case DOCDECL:
42138e84b835c2101206d846f7ab6fc444914661753Jesse Wilson                readDoctype(justOneToken);
4220424d12f9b8a19c4a451282bc8ae974f6109424dNarayan Kamath                if (parsedTopLevelStartTag) {
4230424d12f9b8a19c4a451282bc8ae974f6109424dNarayan Kamath                    throw new XmlPullParserException("Unexpected token", this, null);
4240424d12f9b8a19c4a451282bc8ae974f6109424dNarayan Kamath                }
425bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                break;
4267fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
4277fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            default:
4287fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                throw new XmlPullParserException("Unexpected token", this, null);
429bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            }
430adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
431995a384234fd0ba9233ebcce380628bbe346b911Jesse Wilson            if (depth == 0 && (type == ENTITY_REF || type == TEXT || type == CDSECT)) {
432995a384234fd0ba9233ebcce380628bbe346b911Jesse Wilson                throw new XmlPullParserException("Unexpected token", this, null);
433995a384234fd0ba9233ebcce380628bbe346b911Jesse Wilson            }
434995a384234fd0ba9233ebcce380628bbe346b911Jesse Wilson
435bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            if (justOneToken) {
436bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                return type;
437bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            }
438bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson
439bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            if (type == IGNORABLE_WHITESPACE) {
440bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                text = null;
441fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            }
442bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson
443bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            /*
444bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson             * We've read all that we can of a non-empty text block. Always
445bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson             * report this as text, even if it was a CDATA block or entity
446bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson             * reference.
447bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson             */
4487fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            int peek = peekType(false);
449bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            if (text != null && !text.isEmpty() && peek < TEXT) {
450bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                type = TEXT;
451bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson                return type;
452bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            }
453bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson
454bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            type = peek;
455fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
456fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    }
457adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
458fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    /**
459fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * Reads text until the specified delimiter is encountered. Consumes the
460fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * text and the delimiter.
461fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     *
462fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * @param returnText true to return the read text excluding the delimiter;
463fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     *     false to return null.
464fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     */
465fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private String readUntil(char[] delimiter, boolean returnText)
466fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            throws IOException, XmlPullParserException {
467fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        int start = position;
468fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        StringBuilder result = null;
469adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
470bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        if (returnText && text != null) {
471bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            result = new StringBuilder();
472bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            result.append(text);
473bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        }
474bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson
475fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        search:
476fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        while (true) {
4779349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko            if (position + delimiter.length > limit) {
478fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                if (start < position && returnText) {
479fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    if (result == null) {
480fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                        result = new StringBuilder();
481ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                    }
482fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    result.append(buffer, start, position - start);
483adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
484fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                if (!fillBuffer(delimiter.length)) {
485fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    checkRelaxed(UNEXPECTED_EOF);
486fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    type = COMMENT;
487fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    return null;
488fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
489fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                start = position;
490adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
491adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
492fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            // TODO: replace with Arrays.equals(buffer, position, delimiter, 0, delimiter.length)
493fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            // when the VM has better method inlining
494fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            for (int i = 0; i < delimiter.length; i++) {
495fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                if (buffer[position + i] != delimiter[i]) {
496fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    position++;
497fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    continue search;
498fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
499adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
500fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
501fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            break;
502adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
503adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
504fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        int end = position;
505fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        position += delimiter.length;
506fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
507fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (!returnText) {
508fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            return null;
509fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        } else if (result == null) {
510a78c2aac2a73f001aa00971adfae90af4d6726fbJesse Wilson            return stringPool.get(buffer, start, end - start);
511ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        } else {
512fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            result.append(buffer, start, end - start);
513fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            return result.toString();
514fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
515fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    }
516adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
517fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    /**
518fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * Returns true if an XML declaration was read.
519fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     */
520bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson    private void readXmlDeclaration() throws IOException, XmlPullParserException {
521fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (bufferStartLine != 0 || bufferStartColumn != 0 || position != 0) {
522fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            checkRelaxed("processing instructions must not start with xml");
523fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
524adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
525fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        read(START_PROCESSING_INSTRUCTION);
526086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        parseStartTag(true, true);
527adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
528fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (attributeCount < 1 || !"version".equals(attributes[2])) {
529fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            checkRelaxed("version expected");
530fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
531adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
532fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        version = attributes[3];
533fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
534fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        int pos = 1;
535adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
536fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (pos < attributeCount && "encoding".equals(attributes[2 + 4])) {
537fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            encoding = attributes[3 + 4];
538fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            pos++;
539fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
540adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
541fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (pos < attributeCount && "standalone".equals(attributes[4 * pos + 2])) {
542fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            String st = attributes[3 + 4 * pos];
543fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if ("yes".equals(st)) {
544fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                standalone = Boolean.TRUE;
545fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            } else if ("no".equals(st)) {
546fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                standalone = Boolean.FALSE;
547fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            } else {
548fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                checkRelaxed("illegal standalone value: " + st);
549ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            }
550fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            pos++;
551fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
552adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
553fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (pos != attributeCount) {
554fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            checkRelaxed("unexpected attributes in XML declaration");
555adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
556fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
557fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        isWhitespace = true;
558fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        text = null;
559adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
560adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
561bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson    private String readComment(boolean returnText) throws IOException, XmlPullParserException {
562bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        read(START_COMMENT);
563bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson
564bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        if (relaxed) {
565bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            return readUntil(END_COMMENT, returnText);
566bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        }
567bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson
568bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        String commentText = readUntil(COMMENT_DOUBLE_DASH, returnText);
569bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        if (peekCharacter() != '>') {
570bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson            throw new XmlPullParserException("Comments may not contain --", this, null);
571bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        }
572bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        position++;
573bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        return commentText;
574bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson    }
575bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson
5767fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    /**
5777fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * Read the document's DTD. Although this parser is non-validating, the DTD
5787fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * must be parsed to capture entity values and default attribute values.
5797fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     */
58038e84b835c2101206d846f7ab6fc444914661753Jesse Wilson    private void readDoctype(boolean saveDtdText) throws IOException, XmlPullParserException {
581fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        read(START_DOCTYPE);
58238e84b835c2101206d846f7ab6fc444914661753Jesse Wilson
58338e84b835c2101206d846f7ab6fc444914661753Jesse Wilson        int startPosition = -1;
58438e84b835c2101206d846f7ab6fc444914661753Jesse Wilson        if (saveDtdText) {
58538e84b835c2101206d846f7ab6fc444914661753Jesse Wilson            bufferCapture = new StringBuilder();
58638e84b835c2101206d846f7ab6fc444914661753Jesse Wilson            startPosition = position;
5877fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        }
58838e84b835c2101206d846f7ab6fc444914661753Jesse Wilson        try {
58938e84b835c2101206d846f7ab6fc444914661753Jesse Wilson            skip();
59038e84b835c2101206d846f7ab6fc444914661753Jesse Wilson            rootElementName = readName();
59138e84b835c2101206d846f7ab6fc444914661753Jesse Wilson            readExternalId(true, true);
59238e84b835c2101206d846f7ab6fc444914661753Jesse Wilson            skip();
59338e84b835c2101206d846f7ab6fc444914661753Jesse Wilson            if (peekCharacter() == '[') {
59438e84b835c2101206d846f7ab6fc444914661753Jesse Wilson                readInternalSubset();
59538e84b835c2101206d846f7ab6fc444914661753Jesse Wilson            }
59638e84b835c2101206d846f7ab6fc444914661753Jesse Wilson            skip();
59738e84b835c2101206d846f7ab6fc444914661753Jesse Wilson        } finally {
59838e84b835c2101206d846f7ab6fc444914661753Jesse Wilson            if (saveDtdText) {
59938e84b835c2101206d846f7ab6fc444914661753Jesse Wilson                bufferCapture.append(buffer, 0, position);
60038e84b835c2101206d846f7ab6fc444914661753Jesse Wilson                bufferCapture.delete(0, startPosition);
60138e84b835c2101206d846f7ab6fc444914661753Jesse Wilson                text = bufferCapture.toString();
60238e84b835c2101206d846f7ab6fc444914661753Jesse Wilson                bufferCapture = null;
60338e84b835c2101206d846f7ab6fc444914661753Jesse Wilson            }
60438e84b835c2101206d846f7ab6fc444914661753Jesse Wilson        }
60538e84b835c2101206d846f7ab6fc444914661753Jesse Wilson
6067fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        read('>');
607d343b157ab7b919467f1bdb06b6c5c7b49437049Wojciech Staszkiewicz        skip();
6087fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    }
609fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
6107fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    /**
6117fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * Reads an external ID of one of these two forms:
6127fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   SYSTEM "quoted system name"
6137fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   PUBLIC "quoted public id" "quoted system name"
6147fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *
6157fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * If the system name is not required, this also supports lone public IDs of
6167fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * this form:
6177fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   PUBLIC "quoted public id"
6187fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *
6197fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * Returns true if any ID was read.
6207fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     */
621086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    private boolean readExternalId(boolean requireSystemName, boolean assignFields)
6227fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            throws IOException, XmlPullParserException {
6237fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        skip();
6247fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        int c = peekCharacter();
6257fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
6267fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        if (c == 'S') {
6277fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            read(SYSTEM);
6287fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        } else if (c == 'P') {
6297fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            read(PUBLIC);
6307fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            skip();
631086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson            if (assignFields) {
632086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson                publicId = readQuotedId(true);
633086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson            } else {
634086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson                readQuotedId(false);
635086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson            }
6367fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        } else {
6377fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            return false;
6387fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        }
6397fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
6407fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        skip();
6417fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
6427fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        if (!requireSystemName) {
6437fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            int delimiter = peekCharacter();
6447fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            if (delimiter != '"' && delimiter != '\'') {
6457fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                return true; // no system name!
6467fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            }
6477fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        }
6487fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
649086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        if (assignFields) {
650086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson            systemId = readQuotedId(true);
651086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        } else {
652086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson            readQuotedId(false);
653086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        }
6547fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        return true;
6557fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    }
6567fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
657086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    private static final char[] SINGLE_QUOTE = new char[] { '\'' };
658086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    private static final char[] DOUBLE_QUOTE = new char[] { '"' };
659086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson
6607fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    /**
6617fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * Reads a quoted string, performing no entity escaping of the contents.
6627fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     */
663086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    private String readQuotedId(boolean returnText) throws IOException, XmlPullParserException {
6647fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        int quote = peekCharacter();
665086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        char[] delimiter;
666086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        if (quote == '"') {
667086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson            delimiter = DOUBLE_QUOTE;
668086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        } else if (quote == '\'') {
669086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson            delimiter = SINGLE_QUOTE;
670086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        } else {
6717fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            throw new XmlPullParserException("Expected a quoted string", this, null);
6727fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        }
6737fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        position++;
674086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        return readUntil(delimiter, returnText);
6757fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    }
6767fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
6777fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private void readInternalSubset() throws IOException, XmlPullParserException {
6787fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        read('[');
679adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
680adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        while (true) {
6817fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            skip();
6827fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            if (peekCharacter() == ']') {
6837fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                position++;
6847fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                return;
6857fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            }
6867fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
6877fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            int declarationType = peekType(true);
6887fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            switch (declarationType) {
6897fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            case ELEMENTDECL:
6907fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                readElementDeclaration();
6917fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                break;
6927fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
6937fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            case ATTLISTDECL:
6947fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                readAttributeListDeclaration();
6957fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                break;
6967fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
6977fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            case ENTITYDECL:
6987fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                readEntityDeclaration();
6997fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                break;
7007fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
7017fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            case NOTATIONDECL:
7027fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                readNotationDeclaration();
7037fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                break;
7047fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
7057fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            case PROCESSING_INSTRUCTION:
7067fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                read(START_PROCESSING_INSTRUCTION);
7077fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                readUntil(END_PROCESSING_INSTRUCTION, false);
7087fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                break;
7097fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
7107fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            case COMMENT:
7117fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                readComment(false);
7127fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                break;
7137fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
7147fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            case PARAMETER_ENTITY_REF:
7157fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                throw new XmlPullParserException(
7167fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                        "Parameter entity references are not supported", this, null);
7177fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
7187fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            default:
7197fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                throw new XmlPullParserException("Unexpected token", this, null);
7207fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            }
7217fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        }
7227fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    }
7237fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
7247fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    /**
7257fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * Read an element declaration. This contains a name and a content spec.
7267fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   <!ELEMENT foo EMPTY >
7277fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   <!ELEMENT foo (bar?,(baz|quux)) >
7287fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   <!ELEMENT foo (#PCDATA|bar)* >
7297fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     */
7307fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private void readElementDeclaration() throws IOException, XmlPullParserException {
7317fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        read(START_ELEMENT);
7327fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        skip();
7337fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        readName();
7347fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        readContentSpec();
7357fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        skip();
7367fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        read('>');
7377fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    }
7387fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
7397fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    /**
7407fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * Read an element content spec. This is a regular expression-like pattern
7417fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * of names or other content specs. The following operators are supported:
7427fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   sequence:    (a,b,c)
7437fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   choice:      (a|b|c)
7447fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   optional:    a?
7457fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   one or more: a+
7467fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   any number:  a*
7477fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *
7487fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * The special name '#PCDATA' is permitted but only if it is the first
7497fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * element of the first group:
7507fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   (#PCDATA|a|b)
7517fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *
7527fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * The top-level element must be either a choice, a sequence, or one of the
7537fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * special names EMPTY and ANY.
7547fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     */
7557fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private void readContentSpec() throws IOException, XmlPullParserException {
7567fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        // this implementation is very lenient; it scans for balanced parens only
7577fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        skip();
7587fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        int c = peekCharacter();
7597fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        if (c == '(') {
7607fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            int depth = 0;
7617fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            do {
7627fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                if (c == '(') {
7637fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    depth++;
7647fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                } else if (c == ')') {
7657fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    depth--;
7669349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko                } else if (c == -1) {
7679349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko                    throw new XmlPullParserException(
7689349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko                            "Unterminated element content spec", this, null);
769fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
7707fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                position++;
7717fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                c = peekCharacter();
7727fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            } while (depth > 0);
7737fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
7747fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            if (c == '*' || c == '?' || c == '+') {
7757fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                position++;
7767fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            }
7777fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        } else if (c == EMPTY[0]) {
7787fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            read(EMPTY);
7797fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        } else if (c == ANY[0]) {
7807fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            read(ANY);
7817fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        } else {
7827fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            throw new XmlPullParserException("Expected element content spec", this, null);
7837fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        }
7847fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    }
7857fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
7867fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    /**
7877fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * Reads an attribute list declaration such as the following:
7887fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   <!ATTLIST foo
7897fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *       bar CDATA #IMPLIED
7907fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *       quux (a|b|c) "c"
7917fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *       baz NOTATION (a|b|c) #FIXED "c">
7927fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *
7937fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * Each attribute has a name, type and default.
7947fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *
7957fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * Types are one of the built-in types (CDATA, ID, IDREF, IDREFS, ENTITY,
7967fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * ENTITIES, NMTOKEN, or NMTOKENS), an enumerated type "(list|of|options)"
7977fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * or NOTATION followed by an enumerated type.
7987fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *
7997fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * The default is either #REQUIRED, #IMPLIED, #FIXED, a quoted value, or
8007fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * #FIXED with a quoted value.
8017fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     */
8027fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private void readAttributeListDeclaration() throws IOException, XmlPullParserException {
8037fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        read(START_ATTLIST);
8047fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        skip();
8057fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        String elementName = readName();
8067fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
8077fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        while (true) {
8087fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            skip();
8097fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            int c = peekCharacter();
8107fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            if (c == '>') {
8117fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                position++;
8127fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                return;
813fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            }
814adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
8157fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            // attribute name
8167fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            String attributeName = readName();
817adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
8187fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            // attribute type
8197fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            skip();
8207fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            if (position + 1 >= limit && !fillBuffer(2)) {
8217fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                throw new XmlPullParserException("Malformed attribute list", this, null);
8227fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            }
8237fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            if (buffer[position] == NOTATION[0] && buffer[position + 1] == NOTATION[1]) {
8247fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                read(NOTATION);
8257fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                skip();
8267fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            }
8277fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            c = peekCharacter();
8287fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            if (c == '(') {
8297fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                position++;
8307fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                while (true) {
8317fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    skip();
8327fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    readName();
8337fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    skip();
8347fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    c = peekCharacter();
8357fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    if (c == ')') {
8367fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                        position++;
8377fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                        break;
8387fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    } else if (c == '|') {
8397fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                        position++;
8407fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    } else {
8417fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                        throw new XmlPullParserException("Malformed attribute type", this, null);
8427fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    }
843fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
8447fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            } else {
8457fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                readName();
8467fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            }
8477fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
8487fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            // default value
8497fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            skip();
8507fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            c = peekCharacter();
8517fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            if (c == '#') {
8527fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                position++;
8537fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                c = peekCharacter();
8547fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                if (c == 'R') {
8557fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    read(REQUIRED);
8567fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                } else if (c == 'I') {
8577fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    read(IMPLIED);
8587fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                } else if (c == 'F') {
8597fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    read(FIXED);
8607fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                } else {
8617fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    throw new XmlPullParserException("Malformed attribute type", this, null);
862fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
8637fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                skip();
8647fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                c = peekCharacter();
8657fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            }
8667fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            if (c == '"' || c == '\'') {
8677fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                position++;
8687fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                // TODO: does this do escaping correctly?
869086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson                String value = readValue((char) c, true, true, ValueContext.ATTRIBUTE);
8709349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko                if (peekCharacter() == c) {
8719349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko                    position++;
8729349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko                }
8737fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                defineAttributeDefault(elementName, attributeName, value);
874adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
875fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
8767fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    }
877fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
8787fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private void defineAttributeDefault(String elementName, String attributeName, String value) {
87976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        if (defaultAttributes == null) {
88076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            defaultAttributes = new HashMap<String, Map<String, String>>();
88176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        }
88276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        Map<String, String> elementAttributes = defaultAttributes.get(elementName);
88376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        if (elementAttributes == null) {
88476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            elementAttributes = new HashMap<String, String>();
88576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            defaultAttributes.put(elementName, elementAttributes);
88676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        }
88776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        elementAttributes.put(attributeName, value);
8887fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    }
8897fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
8907fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    /**
8917fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * Read an entity declaration. The value of internal entities are inline:
8927fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   <!ENTITY foo "bar">
8937fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *
8947fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * The values of external entities must be retrieved by URL or path:
8957fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   <!ENTITY foo SYSTEM "http://host/file">
8967fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   <!ENTITY foo PUBLIC "-//Android//Foo//EN" "http://host/file">
8977fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   <!ENTITY foo SYSTEM "../file.png" NDATA png>
8987fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *
8997fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * Entities may be general or parameterized. Parameterized entities are
9007fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * marked by a percent sign. Such entities may only be used in the DTD:
9017fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     *   <!ENTITY % foo "bar">
9027fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     */
9037fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private void readEntityDeclaration() throws IOException, XmlPullParserException {
9047fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        read(START_ENTITY);
9057fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        boolean generalEntity = true;
9067fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
9077fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        skip();
9087fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        if (peekCharacter() == '%') {
9097fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            generalEntity = false;
9107fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            position++;
9117fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            skip();
9127fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        }
9137fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
9147fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        String name = readName();
9157fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
9167fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        skip();
9177fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        int quote = peekCharacter();
918773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson        String entityValue;
9197fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        if (quote == '"' || quote == '\'') {
9207fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            position++;
921773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson            entityValue = readValue((char) quote, true, false, ValueContext.ENTITY_DECLARATION);
9229349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko            if (peekCharacter() == quote) {
9239349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko                position++;
9249349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko            }
925086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        } else if (readExternalId(true, false)) {
926773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson            /*
927773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson             * Map external entities to the empty string. This is dishonest,
928773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson             * but it's consistent with Android's Expat pull parser.
929773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson             */
930773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson            entityValue = "";
9317fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            skip();
9327fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            if (peekCharacter() == NDATA[0]) {
9337fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                read(NDATA);
9347fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                skip();
9357fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                readName();
9367fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            }
937bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        } else {
9387fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            throw new XmlPullParserException("Expected entity value or external ID", this, null);
939adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
9407fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
941773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson        if (generalEntity && processDocDecl) {
942773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson            if (documentEntities == null) {
943773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson                documentEntities = new HashMap<String, char[]>();
944773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson            }
945773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson            documentEntities.put(name, entityValue.toCharArray());
946773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson        }
947773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson
9487fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        skip();
9497fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        read('>');
9507fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    }
9517fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
9527fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private void readNotationDeclaration() throws IOException, XmlPullParserException {
9537fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        read(START_NOTATION);
9547fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        skip();
9557fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        readName();
956086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        if (!readExternalId(false, false)) {
9577fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            throw new XmlPullParserException(
9587fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    "Expected external ID or public ID for notation", this, null);
9597fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        }
9607fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        skip();
9617fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        read('>');
962adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
963adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
964fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private void readEndTag() throws IOException, XmlPullParserException {
965fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        read('<');
966fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        read('/');
967fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        name = readName(); // TODO: pass the expected name in as a hint?
968adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        skip();
969adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        read('>');
970adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
971fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        int sp = (depth - 1) * 4;
972adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
973adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (depth == 0) {
974fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            checkRelaxed("read end tag " + name + " with no tags open");
975adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            type = COMMENT;
976adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return;
977adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
978adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
9793b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson        if (name.equals(elementStack[sp + 3])) {
980ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            namespace = elementStack[sp];
981ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            prefix = elementStack[sp + 1];
982ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            name = elementStack[sp + 2];
9833b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson        } else if (!relaxed) {
9843b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson            throw new XmlPullParserException(
9853b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson                    "expected: /" + elementStack[sp + 3] + " read: " + name, this, null);
986b211e13cf03c07c92ba29829c581747ba0744767Elliott Hughes        }
987adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
988adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
989fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    /**
990fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * Returns the type of the next token.
991fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     */
9927fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    private int peekType(boolean inDeclaration) throws IOException, XmlPullParserException {
993fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (position >= limit && !fillBuffer(1)) {
994fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            return END_DOCUMENT;
995adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
996adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
9977fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        switch (buffer[position]) {
9987fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        case '&':
9997fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            return ENTITY_REF; // &
10007fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        case '<':
10017fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            if (position + 3 >= limit && !fillBuffer(4)) {
1002fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                throw new XmlPullParserException("Dangling <", this, null);
1003fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            }
1004adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
10057fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            switch (buffer[position + 1]) {
10067fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            case '/':
10077fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                return END_TAG; // </
10087fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            case '?':
1009fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                // we're looking for "<?xml " with case insensitivity
1010fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                if ((position + 5 < limit || fillBuffer(6))
1011fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                        && (buffer[position + 2] == 'x' || buffer[position + 2] == 'X')
1012fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                        && (buffer[position + 3] == 'm' || buffer[position + 3] == 'M')
1013fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                        && (buffer[position + 4] == 'l' || buffer[position + 4] == 'L')
1014fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                        && (buffer[position + 5] == ' ')) {
10157fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    return XML_DECLARATION; // <?xml
1016fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                } else {
10177fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    return PROCESSING_INSTRUCTION; // <?
1018fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
10197fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            case '!':
10207fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                switch (buffer[position + 2]) {
10217fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                case 'D':
10227fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    return DOCDECL; // <!D
10237fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                case '[':
10247fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    return CDSECT; // <![
10257fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                case '-':
10267fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    return COMMENT; // <!-
10277fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                case 'E':
10287fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    switch (buffer[position + 3]) {
10297fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    case 'L':
10307fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                        return ELEMENTDECL; // <!EL
10317fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    case 'N':
10327fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                        return ENTITYDECL; // <!EN
10337fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    }
1034866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes                    break;
10357fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                case 'A':
10367fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    return ATTLISTDECL;  // <!A
10377fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                case 'N':
10387fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    return NOTATIONDECL; // <!N
1039fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
1040866e7ae17a3da81a02b0b144e0c9c2b3196d293aElliott Hughes                throw new XmlPullParserException("Unexpected <!", this, null);
10417fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            default:
10427fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                return START_TAG; // <
1043fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            }
10447fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        case '%':
10457fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            return inDeclaration ? PARAMETER_ENTITY_REF : TEXT;
10467fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        default:
1047fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            return TEXT;
1048adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1049adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1050adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1051ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson    /**
1052ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson     * Sets name and attributes
1053ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson     */
1054086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    private void parseStartTag(boolean xmldecl, boolean throwOnResolveFailure)
1055086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson            throws IOException, XmlPullParserException {
1056ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (!xmldecl) {
1057fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            read('<');
1058ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
1059adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        name = readName();
1060adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        attributeCount = 0;
1061adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1062adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        while (true) {
1063adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            skip();
1064adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1065fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (position >= limit && !fillBuffer(1)) {
1066fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                checkRelaxed(UNEXPECTED_EOF);
1067fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                return;
1068fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            }
1069fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1070fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            int c = buffer[position];
1071adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1072adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (xmldecl) {
1073adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if (c == '?') {
1074fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    position++;
1075adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    read('>');
1076adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    return;
1077adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
1078ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            } else {
1079adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                if (c == '/') {
1080adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    degenerated = true;
1081fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    position++;
1082adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    skip();
1083adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    read('>');
1084adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    break;
1085fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                } else if (c == '>') {
1086fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    position++;
1087adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    break;
1088adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
1089adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
1090adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1091adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String attrName = readName();
1092adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1093fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            int i = (attributeCount++) * 4;
1094adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            attributes = ensureCapacity(attributes, i + 4);
109576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            attributes[i] = "";
109676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            attributes[i + 1] = null;
109776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            attributes[i + 2] = attrName;
1098adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1099adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            skip();
1100fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (position >= limit && !fillBuffer(1)) {
1101fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                checkRelaxed(UNEXPECTED_EOF);
1102fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                return;
1103fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            }
1104fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1105fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (buffer[position] == '=') {
1106fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                position++;
1107adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1108adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                skip();
1109fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                if (position >= limit && !fillBuffer(1)) {
1110fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    checkRelaxed(UNEXPECTED_EOF);
1111fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    return;
1112fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
1113fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                char delimiter = buffer[position];
1114adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1115fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                if (delimiter == '\'' || delimiter == '"') {
1116fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    position++;
1117fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                } else if (relaxed) {
1118adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    delimiter = ' ';
1119d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes                } else {
1120fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    throw new XmlPullParserException("attr value delimiter missing!", this, null);
1121adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
1122d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes
1123086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson                attributes[i + 3] = readValue(delimiter, true, throwOnResolveFailure,
1124086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson                        ValueContext.ATTRIBUTE);
1125adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
11269349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko                if (delimiter != ' ' && peekCharacter() == delimiter) {
1127fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    position++; // end quote
1128ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                }
1129fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            } else if (relaxed) {
113076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                attributes[i + 3] = attrName;
1131fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            } else {
1132fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                checkRelaxed("Attr.value missing f. " + attrName);
113376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                attributes[i + 3] = attrName;
1134adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
1135adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1136adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1137fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        int sp = depth++ * 4;
11380424d12f9b8a19c4a451282bc8ae974f6109424dNarayan Kamath        if (depth == 1) {
11390424d12f9b8a19c4a451282bc8ae974f6109424dNarayan Kamath            parsedTopLevelStartTag = true;
11400424d12f9b8a19c4a451282bc8ae974f6109424dNarayan Kamath        }
1141adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        elementStack = ensureCapacity(elementStack, sp + 4);
1142adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        elementStack[sp + 3] = name;
1143adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1144adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (depth >= nspCounts.length) {
1145adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            int[] bigger = new int[depth + 4];
1146adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            System.arraycopy(nspCounts, 0, bigger, 0, nspCounts.length);
1147adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            nspCounts = bigger;
1148adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1149adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1150adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        nspCounts[depth] = nspCounts[depth - 1];
1151adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1152ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (processNsp) {
1153adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            adjustNsp();
1154ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        } else {
1155adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            namespace = "";
1156ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
1157adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
115876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        // For consistency with Expat, add default attributes after fixing namespaces.
115976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        if (defaultAttributes != null) {
116076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            Map<String, String> elementDefaultAttributes = defaultAttributes.get(name);
116176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            if (elementDefaultAttributes != null) {
116276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                for (Map.Entry<String, String> entry : elementDefaultAttributes.entrySet()) {
116376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                    if (getAttributeValue(null, entry.getKey()) != null) {
116476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                        continue; // an explicit value overrides the default
116576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                    }
116676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
116776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                    int i = (attributeCount++) * 4;
116876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                    attributes = ensureCapacity(attributes, i + 4);
116976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                    attributes[i] = "";
117076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                    attributes[i + 1] = null;
117176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                    attributes[i + 2] = entry.getKey();
117276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                    attributes[i + 3] = entry.getValue();
117376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                }
117476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            }
117576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        }
117676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
1177adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        elementStack[sp] = namespace;
1178adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        elementStack[sp + 1] = prefix;
1179adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        elementStack[sp + 2] = name;
1180adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1181adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1182f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes    /**
1183fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * Reads an entity reference from the buffer, resolves it, and writes the
1184fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * resolved entity to {@code out}. If the entity cannot be read or resolved,
1185fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * {@code out} will contain the partial entity reference.
1186ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson     */
1187086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    private void readEntity(StringBuilder out, boolean isEntityToken, boolean throwOnResolveFailure,
1188086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson            ValueContext valueContext) throws IOException, XmlPullParserException {
1189fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        int start = out.length();
1190fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1191fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (buffer[position++] != '&') {
1192fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            throw new AssertionError();
1193fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
1194f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
1195fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        out.append('&');
1196adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1197adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        while (true) {
1198fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            int c = peekCharacter();
1199fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1200b211e13cf03c07c92ba29829c581747ba0744767Elliott Hughes            if (c == ';') {
12013b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson                out.append(';');
1202fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                position++;
1203ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                break;
1204f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes
1205fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            } else if (c >= 128
1206fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || (c >= '0' && c <= '9')
1207fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || (c >= 'a' && c <= 'z')
1208fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || (c >= 'A' && c <= 'Z')
1209fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || c == '_'
1210fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || c == '-'
1211fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || c == '#') {
1212fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                position++;
1213fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                out.append((char) c);
1214fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1215fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            } else if (relaxed) {
1216fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                // intentionally leave the partial reference in 'out'
1217adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                return;
1218adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1219fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            } else {
1220fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                throw new XmlPullParserException("unterminated entity ref", this, null);
1221fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            }
1222adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1223adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
12243b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson        String code = out.substring(start + 1, out.length() - 1);
1225fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1226bbf35ecae9bb5b69fb0d016a57a666d0a9e0f2fcJesse Wilson        if (isEntityToken) {
1227adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            name = code;
1228adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1229adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
12303b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson        if (code.startsWith("#")) {
12313b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson            try {
12323b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson                int c = code.startsWith("#x")
12333b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson                        ? Integer.parseInt(code.substring(2), 16)
12343b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson                        : Integer.parseInt(code.substring(1));
12353b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson                out.delete(start, out.length());
12363b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson                out.appendCodePoint(c);
12373b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson                unresolved = false;
123876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                return;
12393b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson            } catch (NumberFormatException notANumber) {
12403b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson                throw new XmlPullParserException("Invalid character reference: &" + code);
12413b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson            } catch (IllegalArgumentException invalidCodePoint) {
12423b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson                throw new XmlPullParserException("Invalid character reference: &" + code);
12433b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson            }
124476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        }
124576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
124676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        if (valueContext == ValueContext.ENTITY_DECLARATION) {
124776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            // keep the unresolved &code; in the text to resolve later
124876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            return;
124976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        }
125076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
125176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        String defaultEntity = DEFAULT_ENTITIES.get(code);
125276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        if (defaultEntity != null) {
12533b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson            out.delete(start, out.length());
12543b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson            unresolved = false;
125576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            out.append(defaultEntity);
125676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            return;
125776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        }
125876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
125976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        char[] resolved;
126076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        if (documentEntities != null && (resolved = documentEntities.get(code)) != null) {
126176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            out.delete(start, out.length());
126276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            unresolved = false;
126376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            if (processDocDecl) {
126476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                pushContentSource(resolved); // parse the entity as XML
126576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            } else {
126676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                out.append(resolved); // include the entity value as text
12673b8cee4de39f9ff8199b4c1fd46cc8037fa45d15Jesse Wilson            }
126876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            return;
126976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        }
127076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
1271773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson        /*
1272773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson         * The parser skipped an external DTD, and now we've encountered an
1273773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson         * unknown entity that could have been declared there. Map it to the
1274773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson         * empty string. This is dishonest, but it's consistent with Android's
1275773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson         * old ExpatPullParser.
1276773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson         */
1277773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson        if (systemId != null) {
1278773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson            out.delete(start, out.length());
1279773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson            return;
1280773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson        }
1281773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson
128276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        // keep the unresolved entity "&code;" in the text for relaxed clients
128376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        unresolved = true;
1284086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        if (throwOnResolveFailure) {
128576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            checkRelaxed("unresolved: &" + code + ";");
1286adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1287adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1288adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1289ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson    /**
12907fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * Where a value is found impacts how that value is interpreted. For
12917fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * example, in attributes, "\n" must be replaced with a space character. In
12927fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * text, "]]>" is forbidden. In entity declarations, named references are
12937fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * not resolved.
12947fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     */
12957fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    enum ValueContext {
12967fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        ATTRIBUTE,
12977fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        TEXT,
12987fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        ENTITY_DECLARATION
12997fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    }
13007fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson
13017fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson    /**
1302fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * Returns the current text or attribute value. This also has the side
1303fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * effect of setting isWhitespace to false if a non-whitespace character is
1304fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * encountered.
1305fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     *
13067fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson     * @param delimiter {@code <} for text, {@code "} and {@code '} for quoted
1307fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     *     attributes, or a space for unquoted attributes.
1308ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson     */
1309086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    private String readValue(char delimiter, boolean resolveEntities, boolean throwOnResolveFailure,
13107fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson            ValueContext valueContext) throws IOException, XmlPullParserException {
1311fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1312fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        /*
1313fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         * This method returns all of the characters from the current position
1314fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         * through to an appropriate delimiter.
1315fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         *
1316fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         * If we're lucky (which we usually are), we'll return a single slice of
1317fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         * the buffer. This fast path avoids allocating a string builder.
1318fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         *
1319773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson         * There are 6 unlucky characters we could encounter:
1320fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         *  - "&":  entities must be resolved.
1321773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson         *  - "%":  parameter entities are unsupported in entity values.
1322fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         *  - "<":  this isn't permitted in attributes unless relaxed.
1323fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         *  - "]":  this requires a lookahead to defend against the forbidden
1324fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         *          CDATA section delimiter "]]>".
1325fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         *  - "\r": If a "\r" is followed by a "\n", we discard the "\r". If it
1326fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         *          isn't followed by "\n", we replace "\r" with either a "\n"
1327fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         *          in text nodes or a space in attribute values.
1328fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         *  - "\n": In attribute values, "\n" must be replaced with a space.
1329fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         *
1330fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         * We could also get unlucky by needing to refill the buffer midway
1331fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         * through the text.
1332fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson         */
1333fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1334fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        int start = position;
1335fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        StringBuilder result = null;
1336fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1337fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        // if a text section was already started, prefix the start
13387fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson        if (valueContext == ValueContext.TEXT && text != null) {
1339fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            result = new StringBuilder();
1340fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            result.append(text);
1341fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
1342fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1343fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        while (true) {
1344adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1345fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            /*
1346fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson             * Make sure we have at least a single character to read from the
1347fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson             * buffer. This mutates the buffer, so save the partial result
1348fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson             * to the slow path string builder first.
1349fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson             */
1350fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (position >= limit) {
1351fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                if (start < position) {
1352fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    if (result == null) {
1353fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                        result = new StringBuilder();
1354fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    }
1355fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    result.append(buffer, start, position - start);
1356fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
1357fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                if (!fillBuffer(1)) {
1358fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    return result != null ? result.toString() : "";
1359fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
1360fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                start = position;
1361fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            }
1362adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1363fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            char c = buffer[position];
1364adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1365fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (c == delimiter
1366fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || (delimiter == ' ' && (c <= ' ' || c == '>'))
1367fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || c == '&' && !resolveEntities) {
1368ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                break;
1369ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            }
1370adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1371fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (c != '\r'
13727fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                    && (c != '\n' || valueContext != ValueContext.ATTRIBUTE)
1373fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    && c != '&'
1374fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    && c != '<'
1375773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson                    && (c != ']' || valueContext != ValueContext.TEXT)
1376773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson                    && (c != '%' || valueContext != ValueContext.ENTITY_DECLARATION)) {
1377fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                isWhitespace &= (c <= ' ');
1378fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                position++;
1379fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                continue;
1380ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            }
1381adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1382ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            /*
1383fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson             * We've encountered an unlucky character! Convert from fast
1384fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson             * path to slow path if we haven't done so already.
1385ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson             */
1386fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (result == null) {
1387fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                result = new StringBuilder();
13886bcf32ab404c39b85d25430f6df16503ef3526cfElliott Hughes            }
1389fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            result.append(buffer, start, position - start);
1390fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1391fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (c == '\r') {
1392fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                if ((position + 1 < limit || fillBuffer(2)) && buffer[position + 1] == '\n') {
1393fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    position++;
1394fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
13957fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                c = (valueContext == ValueContext.ATTRIBUTE) ? ' ' : '\n';
1396fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1397fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            } else if (c == '\n') {
1398fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                c = ' ';
1399fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1400fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            } else if (c == '&') {
1401fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                isWhitespace = false; // TODO: what if the entity resolves to whitespace?
1402086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson                readEntity(result, false, throwOnResolveFailure, valueContext);
1403fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                start = position;
1404fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                continue;
1405fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1406fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            } else if (c == '<') {
14077fac047a67ee9a0295e4dc798c7c6880ccd83513Jesse Wilson                if (valueContext == ValueContext.ATTRIBUTE) {
1408fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    checkRelaxed("Illegal: \"<\" inside attribute value");
1409fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
1410fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                isWhitespace = false;
1411fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1412fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            } else if (c == ']') {
1413fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                if ((position + 2 < limit || fillBuffer(3))
1414fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                        && buffer[position + 1] == ']' && buffer[position + 2] == '>') {
1415fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    checkRelaxed("Illegal: \"]]>\" outside CDATA section");
1416fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
1417fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                isWhitespace = false;
1418adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1419773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson            } else if (c == '%') {
1420773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson                throw new XmlPullParserException("This parser doesn't support parameter entities",
1421773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson                        this, null);
1422773533775fab54079cebc329d54f6286f5ac16f2Jesse Wilson
1423ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            } else {
1424fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                throw new AssertionError();
1425ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            }
1426adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1427fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            position++;
1428fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            result.append(c);
1429fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            start = position;
1430adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1431adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1432fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (result == null) {
1433a78c2aac2a73f001aa00971adfae90af4d6726fbJesse Wilson            return stringPool.get(buffer, start, position - start);
1434fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        } else {
1435fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            result.append(buffer, start, position - start);
1436fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            return result.toString();
1437ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
1438adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1439adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1440fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private void read(char expected) throws IOException, XmlPullParserException {
1441fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        int c = peekCharacter();
1442fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (c != expected) {
1443fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            checkRelaxed("expected: '" + expected + "' actual: '" + ((char) c) + "'");
14449349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko            if (c == -1) {
14459349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko                return; // On EOF, don't move position beyond limit
14469349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko            }
1447fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
1448fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        position++;
1449fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    }
1450adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1451fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private void read(char[] chars) throws IOException, XmlPullParserException {
14529349ec01a1bebc3b704b400fb00ccfb42e047a02Vladimir Marko        if (position + chars.length > limit && !fillBuffer(chars.length)) {
1453fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            checkRelaxed("expected: '" + new String(chars) + "' but was EOF");
1454fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            return;
1455adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1456adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1457fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        // TODO: replace with Arrays.equals(buffer, position, delimiter, 0, delimiter.length)
1458fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        // when the VM has better method inlining
1459fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        for (int i = 0; i < chars.length; i++) {
1460fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (buffer[position + i] != chars[i]) {
1461fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                checkRelaxed("expected: \"" + new String(chars) + "\" but was \""
1462fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                        + new String(buffer, position, chars.length) + "...\"");
1463fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            }
1464fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
1465adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1466fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        position += chars.length;
1467fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    }
1468adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1469fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    private int peekCharacter() throws IOException, XmlPullParserException {
1470fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (position < limit || fillBuffer(1)) {
1471fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            return buffer[position];
1472adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1473fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        return -1;
1474adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1475adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1476ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson    /**
1477fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * Returns true once {@code limit - position >= minimum}. If the data is
1478fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * exhausted before that many characters are available, this returns
1479fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * false.
1480ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson     */
148176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    private boolean fillBuffer(int minimum) throws IOException, XmlPullParserException {
148276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        // If we've exhausted the current content source, remove it
148376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        while (nextContentSource != null) {
148476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            if (position < limit) {
148576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                throw new XmlPullParserException("Unbalanced entity!", this, null);
148676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            }
148776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            popContentSource();
148876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            if (limit - position >= minimum) {
148976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                return true;
149076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            }
149176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        }
149276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
1493fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        // Before clobbering the old characters, update where buffer starts
1494fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        for (int i = 0; i < position; i++) {
1495fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (buffer[i] == '\n') {
1496fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                bufferStartLine++;
1497fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                bufferStartColumn = 0;
1498ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            } else {
1499fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                bufferStartColumn++;
1500adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
1501fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
1502adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
150338e84b835c2101206d846f7ab6fc444914661753Jesse Wilson        if (bufferCapture != null) {
150438e84b835c2101206d846f7ab6fc444914661753Jesse Wilson            bufferCapture.append(buffer, 0, position);
150538e84b835c2101206d846f7ab6fc444914661753Jesse Wilson        }
150638e84b835c2101206d846f7ab6fc444914661753Jesse Wilson
1507fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (limit != position) {
1508fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            limit -= position;
1509fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            System.arraycopy(buffer, position, buffer, 0, limit);
1510fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        } else {
1511fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            limit = 0;
1512fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
1513adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1514fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        position = 0;
1515fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        int total;
1516fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        while ((total = reader.read(buffer, limit, buffer.length - limit)) != -1) {
1517fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            limit += total;
1518fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (limit >= minimum) {
1519fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                return true;
1520adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
1521adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1522fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        return false;
1523adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1524adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1525fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson    /**
1526fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * Returns an element or attribute name. This is always non-empty for
1527fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     * non-relaxed parsers.
1528fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson     */
1529ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson    private String readName() throws IOException, XmlPullParserException {
1530fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (position >= limit && !fillBuffer(1)) {
1531fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            checkRelaxed("name expected");
1532fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            return "";
1533ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
1534adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1535fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        int start = position;
1536fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        StringBuilder result = null;
1537fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1538fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        // read the first character
1539fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        char c = buffer[position];
1540fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if ((c >= 'a' && c <= 'z')
1541ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                || (c >= 'A' && c <= 'Z')
1542ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                || c == '_'
1543ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                || c == ':'
1544fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                || c >= '\u00c0' // TODO: check the XML spec
1545fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                || relaxed) {
1546fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            position++;
1547fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        } else {
1548fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            checkRelaxed("name expected");
1549fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            return "";
1550fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
1551adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1552fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        while (true) {
1553fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            /*
1554fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson             * Make sure we have at least a single character to read from the
1555fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson             * buffer. This mutates the buffer, so save the partial result
1556fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson             * to the slow path string builder first.
1557fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson             */
1558fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (position >= limit) {
1559fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                if (result == null) {
1560fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    result = new StringBuilder();
1561fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
1562fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                result.append(buffer, start, position - start);
1563fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                if (!fillBuffer(1)) {
1564fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    return result.toString();
1565fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                }
1566fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                start = position;
1567fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            }
1568fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1569fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            // read another character
1570fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            c = buffer[position];
1571fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if ((c >= 'a' && c <= 'z')
1572fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || (c >= 'A' && c <= 'Z')
1573fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || (c >= '0' && c <= '9')
1574fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || c == '_'
1575fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || c == '-'
1576fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || c == ':'
1577fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || c == '.'
1578fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    || c >= '\u00b7') {  // TODO: check the XML spec
1579fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                position++;
1580fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                continue;
1581fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            }
1582fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson
1583fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            // we encountered a non-name character. done!
1584fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (result == null) {
1585a78c2aac2a73f001aa00971adfae90af4d6726fbJesse Wilson                return stringPool.get(buffer, start, position - start);
1586fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            } else {
1587fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                result.append(buffer, start, position - start);
1588fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                return result.toString();
1589fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            }
1590fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
1591adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1592adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
159376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    private void skip() throws IOException, XmlPullParserException {
1594fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        while (position < limit || fillBuffer(1)) {
1595fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            int c = buffer[position];
1596fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (c > ' ') {
1597adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                break;
1598ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            }
1599fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            position++;
1600adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1601adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1602adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1603adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    //  public part starts here...
1604adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1605adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void setInput(Reader reader) throws XmlPullParserException {
1606adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        this.reader = reader;
1607adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1608adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        type = START_DOCUMENT;
16098cfc47957f397cc0066cd923711671533f72374bNeil Fuller        parsedTopLevelStartTag = false;
1610adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        name = null;
1611adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        namespace = null;
1612adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        degenerated = false;
1613adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        attributeCount = -1;
1614adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        encoding = null;
1615adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        version = null;
1616adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        standalone = null;
1617adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1618ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (reader == null) {
1619adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return;
1620ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
1621adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1622fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        position = 0;
1623fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        limit = 0;
1624fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        bufferStartLine = 0;
1625fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        bufferStartColumn = 0;
1626adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        depth = 0;
162776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        documentEntities = null;
1628adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1629adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
16306ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson    public void setInput(InputStream is, String charset) throws XmlPullParserException {
1631fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        position = 0;
1632fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        limit = 0;
16336ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson        boolean detectCharset = (charset == null);
1634adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1635ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (is == null) {
1636cff1616012dc0d56c2da9af2b9b1183e76c7e044Elliott Hughes            throw new IllegalArgumentException("is == null");
1637ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
1638adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1639adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        try {
16406ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson            if (detectCharset) {
1641fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                // read the four bytes looking for an indication of the encoding in use
1642fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                int firstFourBytes = 0;
1643fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                while (limit < 4) {
1644adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    int i = is.read();
1645ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                    if (i == -1) {
1646adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                        break;
1647ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                    }
1648fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    firstFourBytes = (firstFourBytes << 8) | i;
1649fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    buffer[limit++] = (char) i;
1650adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
1651adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1652fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                if (limit == 4) {
1653fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    switch (firstFourBytes) {
16546ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                    case 0x00000FEFF: // UTF-32BE BOM
16556ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        charset = "UTF-32BE";
16566ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        limit = 0;
16576ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        break;
1658adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
16596ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                    case 0x0FFFE0000: // UTF-32LE BOM
16606ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        charset = "UTF-32LE";
16616ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        limit = 0;
16626ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        break;
16636ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson
16646ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                    case 0x0000003c: // '<' in UTF-32BE
16656ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        charset = "UTF-32BE";
16666ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        buffer[0] = '<';
16676ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        limit = 1;
16686ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        break;
16696ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson
16706ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                    case 0x03c000000: // '<' in UTF-32LE
16716ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        charset = "UTF-32LE";
16726ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        buffer[0] = '<';
16736ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        limit = 1;
16746ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        break;
16756ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson
16766ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                    case 0x0003c003f: // "<?" in UTF-16BE
16776ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        charset = "UTF-16BE";
16786ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        buffer[0] = '<';
16796ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        buffer[1] = '?';
16806ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        limit = 2;
16816ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        break;
16826ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson
16836ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                    case 0x03c003f00: // "<?" in UTF-16LE
16846ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        charset = "UTF-16LE";
16856ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        buffer[0] = '<';
16866ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        buffer[1] = '?';
16876ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        limit = 2;
16886ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        break;
16896ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson
16906ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                    case 0x03c3f786d: // "<?xm" in ASCII etc.
16916ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        while (true) {
16926ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                            int i = is.read();
16936ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                            if (i == -1) {
16946ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                                break;
16956ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                            }
16966ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                            buffer[limit++] = (char) i;
16976ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                            if (i == '>') {
16986ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                                String s = new String(buffer, 0, limit);
16996ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                                int i0 = s.indexOf("encoding");
17006ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                                if (i0 != -1) {
17016ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                                    while (s.charAt(i0) != '"' && s.charAt(i0) != '\'') {
17026ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                                        i0++;
1703adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                                    }
17046ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                                    char deli = s.charAt(i0++);
17056ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                                    int i1 = s.indexOf(deli, i0);
17066ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                                    charset = s.substring(i0, i1);
1707adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                                }
17086ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                                break;
1709adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                            }
17106ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        }
17116ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        break;
17126ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson
17136ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                    default:
17146ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        // handle a byte order mark followed by something other than <?
17156ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        if ((firstFourBytes & 0x0ffff0000) == 0x0feff0000) {
17166ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                            charset = "UTF-16BE";
17176ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                            buffer[0] = (char) ((buffer[2] << 8) | buffer[3]);
17186ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                            limit = 1;
17196ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        } else if ((firstFourBytes & 0x0ffff0000) == 0x0fffe0000) {
17206ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                            charset = "UTF-16LE";
17216ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                            buffer[0] = (char) ((buffer[3] << 8) | buffer[2]);
17226ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                            limit = 1;
17236ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        } else if ((firstFourBytes & 0x0ffffff00) == 0x0efbbbf00) {
17246ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                            charset = "UTF-8";
17256ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                            buffer[0] = buffer[3];
17266ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                            limit = 1;
17276ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                        }
1728adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    }
1729adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                }
1730adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
1731adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
17326ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson            if (charset == null) {
17336ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                charset = "UTF-8";
1734ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            }
1735adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
17366ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson            int savedLimit = limit;
17376ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson            setInput(new InputStreamReader(is, charset));
17386ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson            encoding = charset;
17396ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson            limit = savedLimit;
17406ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson
17416ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson            /*
17426ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson             * Skip the optional BOM if we didn't above. This decrements limit
17436ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson             * rather than incrementing position so that <?xml version='1.0'?>
17446ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson             * is still at character 0.
17456ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson             */
17466ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson            if (!detectCharset && peekCharacter() == 0xfeff) {
17476ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                limit--;
17486ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson                System.arraycopy(buffer, 1, buffer, 0, limit);
17496ce8e6ee5da964f724d39655fba0e432cff4c3a6Jesse Wilson            }
1750ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        } catch (Exception e) {
1751fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            throw new XmlPullParserException("Invalid stream or encoding: " + e, this, e);
1752adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1753adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1754adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1755086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    public void close() throws IOException {
1756086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        if (reader != null) {
1757086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson            reader.close();
1758086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        }
1759086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    }
1760086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson
1761adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public boolean getFeature(String feature) {
1762ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature)) {
1763adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return processNsp;
176476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        } else if (FEATURE_RELAXED.equals(feature)) {
1765adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return relaxed;
176676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        } else if (FEATURE_PROCESS_DOCDECL.equals(feature)) {
176776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            return processDocDecl;
1768ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        } else {
1769adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return false;
1770ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
1771adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1772adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1773adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getInputEncoding() {
1774adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return encoding;
1775adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1776adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1777adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void defineEntityReplacementText(String entity, String value)
1778ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            throws XmlPullParserException {
177976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        if (processDocDecl) {
178076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            throw new IllegalStateException(
178176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                    "Entity replacement text may not be defined with DOCTYPE processing enabled.");
178276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        }
178376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        if (reader == null) {
178476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            throw new IllegalStateException(
178576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson                    "Entity replacement text must be defined after setInput()");
1786ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
178776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        if (documentEntities == null) {
178876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            documentEntities = new HashMap<String, char[]>();
178976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        }
179076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        documentEntities.put(entity, value.toCharArray());
1791adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1792adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1793adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public Object getProperty(String property) {
179476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        if (property.equals(PROPERTY_XMLDECL_VERSION)) {
1795adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return version;
179676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        } else if (property.equals(PROPERTY_XMLDECL_STANDALONE)) {
1797adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return standalone;
179876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        } else if (property.equals(PROPERTY_LOCATION)) {
1799d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes            return location != null ? location : reader.toString();
180076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        } else {
180176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            return null;
1802ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
1803adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1804adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1805086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    /**
1806086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson     * Returns the root element's name if it was declared in the DTD. This
1807086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson     * equals the first tag's name for valid documents.
1808086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson     */
1809086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    public String getRootElementName() {
1810086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        return rootElementName;
1811086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    }
1812086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson
1813086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    /**
1814086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson     * Returns the document's system ID if it was declared. This is typically a
1815086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson     * string like {@code http://www.w3.org/TR/html4/strict.dtd}.
1816086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson     */
1817086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    public String getSystemId() {
1818086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        return systemId;
1819086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    }
1820086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson
1821086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    /**
1822086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson     * Returns the document's public ID if it was declared. This is typically a
1823086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson     * string like {@code -//W3C//DTD HTML 4.01//EN}.
1824086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson     */
1825086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    public String getPublicId() {
1826086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson        return publicId;
1827086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson    }
1828086fd0244a54fa5ecf13ea66d49b22b36d7d456eJesse Wilson
1829adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getNamespaceCount(int depth) {
1830ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (depth > this.depth) {
1831adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new IndexOutOfBoundsException();
1832ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
1833adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return nspCounts[depth];
1834adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1835adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1836adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getNamespacePrefix(int pos) {
1837fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        return nspStack[pos * 2];
1838adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1839adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1840adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getNamespaceUri(int pos) {
1841fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        return nspStack[(pos * 2) + 1];
1842adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1843adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1844adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getNamespace(String prefix) {
1845ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if ("xml".equals(prefix)) {
1846adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return "http://www.w3.org/XML/1998/namespace";
1847ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
1848ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if ("xmlns".equals(prefix)) {
1849adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            return "http://www.w3.org/2000/xmlns/";
1850ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
1851adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1852adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        for (int i = (getNamespaceCount(depth) << 1) - 2; i >= 0; i -= 2) {
1853adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (prefix == null) {
1854ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                if (nspStack[i] == null) {
1855adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                    return nspStack[i + 1];
1856ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                }
1857ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            } else if (prefix.equals(nspStack[i])) {
1858adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                return nspStack[i + 1];
1859ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            }
1860adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1861adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return null;
1862adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1863adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1864adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getDepth() {
1865adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return depth;
1866adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1867adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1868adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getPositionDescription() {
1869ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        StringBuilder buf = new StringBuilder(type < TYPES.length ? TYPES[type] : "unknown");
1870adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        buf.append(' ');
1871adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1872adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (type == START_TAG || type == END_TAG) {
1873ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            if (degenerated) {
1874adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                buf.append("(empty) ");
1875ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            }
1876adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buf.append('<');
1877ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            if (type == END_TAG) {
1878adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                buf.append('/');
1879ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            }
1880adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1881ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            if (prefix != null) {
1882adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                buf.append("{" + namespace + "}" + prefix + ":");
1883ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            }
1884adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buf.append(name);
1885adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1886fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            int cnt = attributeCount * 4;
1887adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            for (int i = 0; i < cnt; i += 4) {
1888adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                buf.append(' ');
1889ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                if (attributes[i + 1] != null) {
1890fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    buf.append("{" + attributes[i] + "}" + attributes[i + 1] + ":");
1891ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                }
1892adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                buf.append(attributes[i + 2] + "='" + attributes[i + 3] + "'");
1893adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            }
1894adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1895adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buf.append('>');
1896ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        } else if (type == IGNORABLE_WHITESPACE) {
1897ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            ;
1898ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        } else if (type != TEXT) {
1899adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buf.append(getText());
1900ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        } else if (isWhitespace) {
1901adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buf.append("(whitespace)");
1902ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        } else {
1903adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            String text = getText();
1904ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            if (text.length() > 16) {
1905adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                text = text.substring(0, 16) + "...";
1906ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            }
1907adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            buf.append(text);
1908adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
1909adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1910fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        buf.append("@" + getLineNumber() + ":" + getColumnNumber());
1911ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (location != null) {
1912d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes            buf.append(" in ");
1913d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes            buf.append(location);
1914ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        } else if (reader != null) {
1915d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes            buf.append(" in ");
1916d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes            buf.append(reader.toString());
1917d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes        }
1918adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return buf.toString();
1919adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1920adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1921adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getLineNumber() {
1922fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        int result = bufferStartLine;
1923fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        for (int i = 0; i < position; i++) {
1924fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (buffer[i] == '\n') {
1925fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                result++;
1926fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            }
1927fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
1928fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        return result + 1; // the first line is '1'
1929adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1930adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1931adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getColumnNumber() {
1932fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        int result = bufferStartColumn;
1933fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        for (int i = 0; i < position; i++) {
1934fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            if (buffer[i] == '\n') {
1935fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                result = 0;
1936fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            } else {
1937fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                result++;
1938fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            }
1939fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
1940fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        return result + 1; // the first column is '1'
1941adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1942adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1943adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public boolean isWhitespace() throws XmlPullParserException {
1944ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (type != TEXT && type != IGNORABLE_WHITESPACE && type != CDSECT) {
1945fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            throw new XmlPullParserException(ILLEGAL_TYPE, this, null);
1946ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
1947adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return isWhitespace;
1948adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1949adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1950adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getText() {
1951fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (type < TEXT || (type == ENTITY_REF && unresolved)) {
1952fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            return null;
1953fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        } else if (text == null) {
1954fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            return "";
1955fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        } else {
1956fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            return text;
1957fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
1958adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1959adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1960adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public char[] getTextCharacters(int[] poslen) {
1961fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        String text = getText();
1962fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        if (text == null) {
1963fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            poslen[0] = -1;
1964fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            poslen[1] = -1;
1965fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            return null;
1966fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        }
1967fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        char[] result = text.toCharArray();
1968fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        poslen[0] = 0;
1969fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        poslen[1] = result.length;
1970fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        return result;
1971adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1972adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1973adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getNamespace() {
1974adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return namespace;
1975adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1976adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1977adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getName() {
1978adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return name;
1979adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1980adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1981adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getPrefix() {
1982adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return prefix;
1983adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1984adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1985adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public boolean isEmptyElementTag() throws XmlPullParserException {
1986ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (type != START_TAG) {
1987fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            throw new XmlPullParserException(ILLEGAL_TYPE, this, null);
1988ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
1989adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return degenerated;
1990adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1991adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1992adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getAttributeCount() {
1993adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return attributeCount;
1994adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1995adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
1996adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getAttributeType(int index) {
1997adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return "CDATA";
1998adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
1999adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2000adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public boolean isAttributeDefault(int index) {
2001adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return false;
2002adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
2003adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2004adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getAttributeNamespace(int index) {
2005ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (index >= attributeCount) {
2006adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new IndexOutOfBoundsException();
2007ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
2008fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        return attributes[index * 4];
2009adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
2010adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2011adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getAttributeName(int index) {
2012ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (index >= attributeCount) {
2013adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new IndexOutOfBoundsException();
2014ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
2015fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        return attributes[(index * 4) + 2];
2016adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
2017adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2018adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getAttributePrefix(int index) {
2019ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (index >= attributeCount) {
2020adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new IndexOutOfBoundsException();
2021ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
2022fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        return attributes[(index * 4) + 1];
2023adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
2024adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2025adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getAttributeValue(int index) {
2026ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (index >= attributeCount) {
2027adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            throw new IndexOutOfBoundsException();
2028ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
2029fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        return attributes[(index * 4) + 3];
2030adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
2031adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2032adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String getAttributeValue(String namespace, String name) {
2033fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson        for (int i = (attributeCount * 4) - 4; i >= 0; i -= 4) {
2034adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            if (attributes[i + 2].equals(name)
2035ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                    && (namespace == null || attributes[i].equals(namespace))) {
2036adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project                return attributes[i + 3];
2037ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            }
2038adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        }
2039adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2040adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return null;
2041adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
2042adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2043adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int getEventType() throws XmlPullParserException {
2044adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return type;
2045adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
2046adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2047adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    // utility methods to make XML parsing easier ...
2048adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2049adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public int nextTag() throws XmlPullParserException, IOException {
2050adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        next();
2051ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (type == TEXT && isWhitespace) {
2052adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            next();
2053ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
2054adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2055ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (type != END_TAG && type != START_TAG) {
2056fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            throw new XmlPullParserException("unexpected type", this, null);
2057ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
2058adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2059adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return type;
2060adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
2061adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2062adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public void require(int type, String namespace, String name)
2063ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson            throws XmlPullParserException, IOException {
2064adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (type != this.type
2065ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                || (namespace != null && !namespace.equals(getNamespace()))
2066ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson                || (name != null && !name.equals(getName()))) {
2067fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            throw new XmlPullParserException(
2068fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson                    "expected: " + TYPES[type] + " {" + namespace + "}" + name, this, null);
2069ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
2070adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
2071adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2072adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    public String nextText() throws XmlPullParserException, IOException {
2073ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (type != START_TAG) {
2074fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            throw new XmlPullParserException("precondition: START_TAG", this, null);
2075ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
2076adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2077adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        next();
2078adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2079adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        String result;
2080adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        if (type == TEXT) {
2081adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            result = getText();
2082adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            next();
2083ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        } else {
2084adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            result = "";
2085ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
2086adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2087ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (type != END_TAG) {
2088fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            throw new XmlPullParserException("END_TAG expected", this, null);
2089ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
2090adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2091adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project        return result;
2092adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
2093adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2094ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson    public void setFeature(String feature, boolean value) throws XmlPullParserException {
2095ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        if (XmlPullParser.FEATURE_PROCESS_NAMESPACES.equals(feature)) {
2096adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            processNsp = value;
209776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        } else if (XmlPullParser.FEATURE_PROCESS_DOCDECL.equals(feature)) {
209876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            processDocDecl = value;
209976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        } else if (FEATURE_RELAXED.equals(feature)) {
2100adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project            relaxed = value;
2101ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        } else {
2102fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            throw new XmlPullParserException("unsupported feature: " + feature, this, null);
2103ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson        }
2104adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
2105adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project
2106ccd79e2bb784cdbd89e90a0e4301a707091d446dJesse Wilson    public void setProperty(String property, Object value) throws XmlPullParserException {
210776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        if (property.equals(PROPERTY_LOCATION)) {
2108fda724de28fe86804e6ef6a0afd7ae5be1529083Jesse Wilson            location = String.valueOf(value);
2109d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes        } else {
2110d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes            throw new XmlPullParserException("unsupported property: " + property);
2111d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes        }
2112adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project    }
211376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
211476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    /**
211576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * A chain of buffers containing XML content. Each content source contains
211676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * the parser's primary read buffer or the characters of entities actively
211776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * being parsed.
211876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     *
211976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * <p>For example, note the buffers needed to parse this document:
212076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * <pre>   {@code
212176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     *   <!DOCTYPE foo [
212276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     *       <!ENTITY baz "ghi">
212376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     *       <!ENTITY bar "def &baz; jkl">
212476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     *   ]>
212576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     *   <foo>abc &bar; mno</foo>
212676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * }</pre>
212776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     *
212876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * <p>Things get interesting when the bar entity is encountered. At that
212976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * point two buffers are active:
213076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * <ol>
213176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * <li>The value for the bar entity, containing {@code "def &baz; jkl"}
213276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * <li>The parser's primary read buffer, containing {@code " mno</foo>"}
213376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * </ol>
213476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * <p>The parser will return the characters {@code "def "} from the bar
213576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * entity's buffer, and then it will encounter the baz entity. To handle
213676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * that, three buffers will be active:
213776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * <ol>
213876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * <li>The value for the baz entity, containing {@code "ghi"}
213976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * <li>The remaining value for the bar entity, containing {@code " jkl"}
214076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * <li>The parser's primary read buffer, containing {@code " mno</foo>"}
214176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * </ol>
214276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * <p>The parser will then return the characters {@code ghi jkl mno} in that
214376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * sequence by reading each buffer in sequence.
214476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     */
214576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    static class ContentSource {
214676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        private final ContentSource next;
214776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        private final char[] buffer;
214876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        private final int position;
214976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        private final int limit;
215076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        ContentSource(ContentSource next, char[] buffer, int position, int limit) {
215176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            this.next = next;
215276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            this.buffer = buffer;
215376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            this.position = position;
215476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson            this.limit = limit;
215576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        }
215676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    }
215776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
215876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    /**
215976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * Prepends the characters of {@code newBuffer} to be read before the
216076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * current buffer.
216176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     */
216276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    private void pushContentSource(char[] newBuffer) {
216376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        nextContentSource = new ContentSource(nextContentSource, buffer, position, limit);
216476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        buffer = newBuffer;
216576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        position = 0;
216676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        limit = newBuffer.length;
216776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    }
216876c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson
216976c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    /**
217076c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     * Replaces the current exhausted buffer with the next buffer in the chain.
217176c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson     */
217276c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    private void popContentSource() {
217376c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        buffer = nextContentSource.buffer;
217476c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        position = nextContentSource.position;
217576c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        limit = nextContentSource.limit;
217676c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson        nextContentSource = nextContentSource.next;
217776c85883787791ba605a51d7bea3cfc27b5c5d95Jesse Wilson    }
2178adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project}
2179