1e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne/*
2e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * Copyright (C) 2011 The Android Open Source Project
3e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
4e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * Licensed under the Apache License, Version 2.0 (the "License");
5e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * you may not use this file except in compliance with the License.
6e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * You may obtain a copy of the License at
7e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
8e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *      http://www.apache.org/licenses/LICENSE-2.0
9e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
10e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * Unless required by applicable law or agreed to in writing, software
11e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * distributed under the License is distributed on an "AS IS" BASIS,
12e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * See the License for the specific language governing permissions and
14e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * limitations under the License.
15e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne */
16e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
17e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunnepackage com.example.android.xmladapters;
18e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
19e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport org.apache.http.HttpEntity;
20e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport org.apache.http.HttpResponse;
21e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport org.apache.http.HttpStatus;
22e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport org.apache.http.client.methods.HttpGet;
23e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
24e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport android.content.ContentProvider;
25e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport android.content.ContentResolver;
26e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport android.content.ContentValues;
27e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport android.content.pm.PackageManager.NameNotFoundException;
28e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport android.content.res.Resources;
29e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport android.database.Cursor;
30e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport android.database.MatrixCursor;
31e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport android.net.Uri;
32e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport android.net.http.AndroidHttpClient;
33e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport android.text.TextUtils;
34e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport android.util.Log;
35e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport android.widget.CursorAdapter;
36e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
37e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport org.xmlpull.v1.XmlPullParser;
38e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport org.xmlpull.v1.XmlPullParserException;
39e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport org.xmlpull.v1.XmlPullParserFactory;
40e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
41e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport java.io.FileNotFoundException;
42e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport java.io.IOException;
43e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport java.io.InputStream;
44e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport java.util.BitSet;
45e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport java.util.List;
46e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport java.util.Stack;
47e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunneimport java.util.regex.Pattern;
48e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
49e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne/**
50e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
51e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * A read-only content provider which extracts data out of an XML document.
52e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
53e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <p>A XPath-like selection pattern is used to select some nodes in the XML document. Each such
54e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * node will create a row in the {@link Cursor} result.</p>
55e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
56e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * Each row is then populated with columns that are also defined as XPath-like projections. These
57e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * projections fetch attributes values or text in the matching row node or its children.
58e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
59e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <p>To add this provider in your application, you should add its declaration to your application
60e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * manifest:
61e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <pre class="prettyprint">
62968c2e4918246ef2c9530fd024f3e40932283cacGilles Debunne * &lt;provider android:name="XmlDocumentProvider" android:authorities="xmldocument" /&gt;
63e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * </pre>
64e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * </p>
65e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
66e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <h2>Node selection syntax</h2>
67e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * The node selection syntax is made of the concatenation of an arbitrary number (at least one) of
68e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <code>/node_name</code> node selection patterns.
69e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
70e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <p>The <code>/root/child1/child2</code> pattern will for instance match all nodes named
71e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <code>child2</code> which are children of a node named <code>child1</code> which are themselves
72e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * children of a root node named <code>root</code>.</p>
73e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
74e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * Any <code>/</code> separator in the previous expression can be replaced by a <code>//</code>
75e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * separator instead, which indicated a <i>descendant</i> instead of a child.
76e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
77e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <p>The <code>//node1//node2</code> pattern will for instance match all nodes named
78e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <code>node2</code> which are descendant of a node named <code>node1</code> located anywhere in
79e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * the document hierarchy.</p>
80e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
81e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * Node names can contain namespaces in the form <code>namespace:node</code>.
82e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
83e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <h2>Projection syntax</h2>
84e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * For every selected node, the projection will then extract actual data from this node and its
85e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * descendant.
86e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
87e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <p>Use a syntax similar to the selection syntax described above to select the text associated
88e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * with a child of the selected node. The implicit root of this projection pattern is the selected
89e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * node. <code>/</code> will hence refer to the text of the selected node, while
90e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <code>/child1</code> will fetch the text of its child named <code>child1</code> and
91e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <code>//child1</code> will match any <i>descendant</i> named <code>child1</code>. If several
92e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * nodes match the projection pattern, their texts are appended as a result.</p>
93e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
94e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * A projection can also fetch any node attribute by appending a <code>@attribute_name</code>
95e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * pattern to the previously described syntax. <code>//child1@price</code> will for instance match
96e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * the attribute <code>price</code> of any <code>child1</code> descendant.
97e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
98e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <p>If a projection does not match any node/attribute, its associated value will be an empty
99e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * string.</p>
100e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
101e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <h2>Example</h2>
102e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * Using the following XML document:
103e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <pre class="prettyprint">
104e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * &lt;library&gt;
105e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *   &lt;book id="EH94"&gt;
106e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *     &lt;title&gt;The Old Man and the Sea&lt;/title&gt;
107e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *     &lt;author&gt;Ernest Hemingway&lt;/author&gt;
108e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *   &lt;/book&gt;
109e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *   &lt;book id="XX10"&gt;
110e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *     &lt;title&gt;The Arabian Nights: Tales of 1,001 Nights&lt;/title&gt;
111e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *   &lt;/book&gt;
112e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *   &lt;no-id&gt;
113e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *     &lt;book&gt;
114e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *       &lt;title&gt;Animal Farm&lt;/title&gt;
115e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *       &lt;author&gt;George Orwell&lt;/author&gt;
116e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *     &lt;/book&gt;
117e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *   &lt;/no-id&gt;
118e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * &lt;/library&gt;
119e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * </pre>
120e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * A selection pattern of <code>/library//book</code> will match the three book entries (while
121e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <code>/library/book</code> will only match the first two ones).
122e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne *
123e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <p>Defining the projections as <code>/title</code>, <code>/author</code> and <code>@id</code>
124e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * will retrieve the associated data. Note that the author of the second book as well as the id of
125e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * the third are empty strings.
126e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne */
127e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunnepublic class XmlDocumentProvider extends ContentProvider {
128e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    /*
129e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * Ideas for improvement:
130e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * - Expand XPath-like syntax to allow for [nb] child number selector
131e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * - Address the starting . bug in AbstractCursor which prevents a true XPath syntax.
132e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * - Provide an alternative to concatenation when several node match (list-like).
133e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * - Support namespaces in attribute names.
134e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * - Incremental Cursor creation, pagination
135e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     */
136e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    private static final String LOG_TAG = "XmlDocumentProvider";
137e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    private AndroidHttpClient mHttpClient;
138e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
139e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    @Override
140e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    public boolean onCreate() {
141e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        return true;
142e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    }
143e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
144e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    /**
145e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * Query data from the XML document referenced in the URI.
146e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     *
147e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * <p>The XML document can be a local resource or a file that will be downloaded from the
148e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * Internet. In the latter case, your application needs to request the INTERNET permission in
149e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * its manifest.</p>
150e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     *
151e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * The URI will be of the form <code>content://xmldocument/?resource=R.xml.myFile</code> for a
152e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * local resource. <code>xmldocument</code> should match the authority declared for this
153e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * provider in your manifest. Internet documents are referenced using
154e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * <code>content://xmldocument/?url=</code> followed by an encoded version of the URL of your
155e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * document (see {@link Uri#encode(String)}).
156e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     *
157e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * <p>The number of columns of the resulting Cursor is equal to the size of the projection
158e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * array plus one, named <code>_id</code> which will contain a unique row id (allowing the
159e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * Cursor to be used with a {@link CursorAdapter}). The other columns' names are the projection
160e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * patterns.</p>
161e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     *
162e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * @param uri The URI of your local resource or Internet document.
163e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * @param projection A set of patterns that will be used to extract data from each selected
164e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * node. See class documentation for pattern syntax.
165e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * @param selection A selection pattern which will select the nodes that will create the
166e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * Cursor's rows. See class documentation for pattern syntax.
167e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * @param selectionArgs This parameter is ignored.
168e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * @param sortOrder The row order in the resulting cursor is determined from the node order in
169e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * the XML document. This parameter is ignored.
170e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * @return A Cursor or null in case of error.
171e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     */
172e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    @Override
173e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
174e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            String sortOrder) {
175e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
176e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        XmlPullParser parser = null;
177e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        mHttpClient = null;
178e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
179e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        final String url = uri.getQueryParameter("url");
180e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        if (url != null) {
181e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            parser = getUriXmlPullParser(url);
182e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        } else {
183e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            final String resource = uri.getQueryParameter("resource");
184e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            if (resource != null) {
185e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                Uri resourceUri = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" +
186e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        getContext().getPackageName() + "/" + resource);
187e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                parser = getResourceXmlPullParser(resourceUri);
188e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            }
189e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        }
190e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
191e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        if (parser != null) {
192e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            XMLCursor xmlCursor = new XMLCursor(selection, projection);
193e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            try {
194e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                xmlCursor.parseWith(parser);
195e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                return xmlCursor;
196e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            } catch (IOException e) {
197e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                Log.w(LOG_TAG, "I/O error while parsing XML " + uri, e);
198e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            } catch (XmlPullParserException e) {
199e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                Log.w(LOG_TAG, "Error while parsing XML " + uri, e);
200e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            } finally {
201e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                if (mHttpClient != null) {
202e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    mHttpClient.close();
203e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                }
204e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            }
205e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        }
206e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
207e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        return null;
208e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    }
209e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
210e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    /**
211e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * Creates an XmlPullParser for the provided URL. Can be overloaded to provide your own parser.
212e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * @param url The URL of the XML document that is to be parsed.
213e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * @return An XmlPullParser on this document.
214e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     */
215e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    protected XmlPullParser getUriXmlPullParser(String url) {
216e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        XmlPullParser parser = null;
217e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        try {
218e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
219e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            factory.setNamespaceAware(true);
220e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            parser = factory.newPullParser();
221e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        } catch (XmlPullParserException e) {
222e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            Log.e(LOG_TAG, "Unable to create XmlPullParser", e);
223e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            return null;
224e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        }
225e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
226e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        InputStream inputStream = null;
227e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        try {
228e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            final HttpGet get = new HttpGet(url);
229e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            mHttpClient = AndroidHttpClient.newInstance("Android");
230e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            HttpResponse response = mHttpClient.execute(get);
231e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
232e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                final HttpEntity entity = response.getEntity();
233e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                if (entity != null) {
234e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    inputStream = entity.getContent();
235e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                }
236e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            }
237e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        } catch (IOException e) {
238e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            Log.w(LOG_TAG, "Error while retrieving XML file " + url, e);
239e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            return null;
240e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        }
241e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
242e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        try {
243e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            parser.setInput(inputStream, null);
244e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        } catch (XmlPullParserException e) {
245e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            Log.w(LOG_TAG, "Error while reading XML file from " + url, e);
246e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            return null;
247e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        }
248e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
249e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        return parser;
250e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    }
251e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
252e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    /**
253e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * Creates an XmlPullParser for the provided local resource. Can be overloaded to provide your
254e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * own parser.
255e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * @param resourceUri A fully qualified resource name referencing a local XML resource.
256e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * @return An XmlPullParser on this resource.
257e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     */
258e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    protected XmlPullParser getResourceXmlPullParser(Uri resourceUri) {
259e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        //OpenResourceIdResult resourceId;
260e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        try {
261e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            String authority = resourceUri.getAuthority();
262e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            Resources r;
263e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            if (TextUtils.isEmpty(authority)) {
264e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                throw new FileNotFoundException("No authority: " + resourceUri);
265e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            } else {
266e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                try {
267e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    r = getContext().getPackageManager().getResourcesForApplication(authority);
268e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                } catch (NameNotFoundException ex) {
269e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    throw new FileNotFoundException("No package found for authority: " + resourceUri);
270e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                }
271e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            }
272e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            List<String> path = resourceUri.getPathSegments();
273e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            if (path == null) {
274e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                throw new FileNotFoundException("No path: " + resourceUri);
275e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            }
276e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            int len = path.size();
277e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            int id;
278e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            if (len == 1) {
279e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                try {
280e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    id = Integer.parseInt(path.get(0));
281e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                } catch (NumberFormatException e) {
282e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    throw new FileNotFoundException("Single path segment is not a resource ID: " + resourceUri);
283e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                }
284e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            } else if (len == 2) {
285e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                id = r.getIdentifier(path.get(1), path.get(0), authority);
286e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            } else {
287e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                throw new FileNotFoundException("More than two path segments: " + resourceUri);
288e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            }
289e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            if (id == 0) {
290e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                throw new FileNotFoundException("No resource found for: " + resourceUri);
291e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            }
292e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
293e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            return r.getXml(id);
294e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        } catch (FileNotFoundException e) {
295e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            Log.w(LOG_TAG, "XML resource not found: " + resourceUri.toString(), e);
296e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            return null;
297e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        }
298e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    }
299e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
300e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    /**
301e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * Returns "vnd.android.cursor.dir/xmldoc".
302e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     */
303e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    @Override
304e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    public String getType(Uri uri) {
305e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        return "vnd.android.cursor.dir/xmldoc";
306e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    }
307e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
308e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    /**
309e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * This ContentProvider is read-only. This method throws an UnsupportedOperationException.
310e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     **/
311e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    @Override
312e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    public Uri insert(Uri uri, ContentValues values) {
313e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        throw new UnsupportedOperationException();
314e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    }
315e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
316e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    /**
317e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * This ContentProvider is read-only. This method throws an UnsupportedOperationException.
318e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     **/
319e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    @Override
320e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    public int delete(Uri uri, String selection, String[] selectionArgs) {
321e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        throw new UnsupportedOperationException();
322e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    }
323e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
324e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    /**
325e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     * This ContentProvider is read-only. This method throws an UnsupportedOperationException.
326e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne     **/
327e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    @Override
328e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
329e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        throw new UnsupportedOperationException();
330e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    }
331e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
332e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    private static class XMLCursor extends MatrixCursor {
333e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        private final Pattern mSelectionPattern;
334e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        private Pattern[] mProjectionPatterns;
335e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        private String[] mAttributeNames;
336e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        private String[] mCurrentValues;
337e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        private BitSet[] mActiveTextDepthMask;
338e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        private final int mNumberOfProjections;
339e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
340e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        public XMLCursor(String selection, String[] projections) {
341e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            super(projections);
342e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            // The first column in projections is used for the _ID
343e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            mNumberOfProjections = projections.length - 1;
344e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            mSelectionPattern = createPattern(selection);
345e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            createProjectionPattern(projections);
346e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        }
347e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
348e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        private Pattern createPattern(String input) {
349e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            String pattern = input.replaceAll("//", "/(.*/|)").replaceAll("^/", "^/") + "$";
350e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            return Pattern.compile(pattern);
351e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        }
352e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
353e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        private void createProjectionPattern(String[] projections) {
354e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            mProjectionPatterns = new Pattern[mNumberOfProjections];
355e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            mAttributeNames = new String[mNumberOfProjections];
356e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            mActiveTextDepthMask = new BitSet[mNumberOfProjections];
357e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            // Add a column to store _ID
358e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            mCurrentValues = new String[mNumberOfProjections + 1];
359e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
360e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            for (int i=0; i<mNumberOfProjections; i++) {
361e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                mActiveTextDepthMask[i] = new BitSet();
362e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                String projection = projections[i + 1]; // +1 to skip the _ID column
363e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                int atIndex = projection.lastIndexOf('@', projection.length());
364e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                if (atIndex >= 0) {
365e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    mAttributeNames[i] = projection.substring(atIndex+1);
366e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    projection = projection.substring(0, atIndex);
367e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                } else {
368e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    mAttributeNames[i] = null;
369e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                }
370e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
371e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                // Conforms to XPath standard: reference to local context starts with a .
372e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                if (projection.charAt(0) == '.') {
373e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    projection = projection.substring(1);
374e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                }
375e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                mProjectionPatterns[i] = createPattern(projection);
376e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            }
377e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        }
378e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
379e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        public void parseWith(XmlPullParser parser) throws IOException, XmlPullParserException {
380e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            StringBuilder path = new StringBuilder();
381e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            Stack<Integer> pathLengthStack = new Stack<Integer>();
382e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
383e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            // There are two parsing mode: in root mode, rootPath is updated and nodes matching
384e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            // selectionPattern are searched for and currentNodeDepth is negative.
385e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            // When a node matching selectionPattern is found, currentNodeDepth is set to 0 and
386e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            // updated as children are parsed and projectionPatterns are searched in nodePath.
387e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            int currentNodeDepth = -1;
388e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
389e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            // Index where local selected node path starts from in path
390e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            int currentNodePathStartIndex = 0;
391e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
392e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            int eventType = parser.getEventType();
393e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            while (eventType != XmlPullParser.END_DOCUMENT) {
394e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
395e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                if (eventType == XmlPullParser.START_TAG) {
396e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    // Update path
397e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    pathLengthStack.push(path.length());
398e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    path.append('/');
399e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    String prefix = null;
400e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    try {
401e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        // getPrefix is not supported by local Xml resource parser
402e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        prefix = parser.getPrefix();
403e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    } catch (RuntimeException e) {
404e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        prefix = null;
405e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    }
406e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    if (prefix != null) {
407e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        path.append(prefix);
408e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        path.append(':');
409e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    }
410e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    path.append(parser.getName());
411e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
412e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    if (currentNodeDepth >= 0) {
413e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        currentNodeDepth++;
414e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    } else {
415e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        // A node matching selection is found: initialize child parsing mode
416e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        if (mSelectionPattern.matcher(path.toString()).matches()) {
417e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                            currentNodeDepth = 0;
418e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                            currentNodePathStartIndex = path.length();
419e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                            mCurrentValues[0] = Integer.toString(getCount()); // _ID
420e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                            for (int i = 0; i < mNumberOfProjections; i++) {
421e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                                // Reset values to default (empty string)
422e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                                mCurrentValues[i + 1] = "";
423e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                                mActiveTextDepthMask[i].clear();
424e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                            }
425e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        }
426e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    }
427e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
428e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    // This test has to be separated from the previous one as currentNodeDepth can
429e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    // be modified above (when a node matching selection is found).
430e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    if (currentNodeDepth >= 0) {
431e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        final String localNodePath = path.substring(currentNodePathStartIndex);
432e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        for (int i = 0; i < mNumberOfProjections; i++) {
433e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                            if (mProjectionPatterns[i].matcher(localNodePath).matches()) {
434e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                                String attribute = mAttributeNames[i];
435e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                                if (attribute != null) {
436e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                                    mCurrentValues[i + 1] =
437e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                                        parser.getAttributeValue(null, attribute);
438e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                                } else {
439e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                                    mActiveTextDepthMask[i].set(currentNodeDepth, true);
440e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                                }
441e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                            }
442e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        }
443e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    }
444e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
445e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                } else if (eventType == XmlPullParser.END_TAG) {
446e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    // Pop last node from path
447e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    final int length = pathLengthStack.pop();
448e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    path.setLength(length);
449e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
450e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    if (currentNodeDepth >= 0) {
451e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        if (currentNodeDepth == 0) {
452e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                            // Leaving a selection matching node: add a new row with results
453e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                            addRow(mCurrentValues);
454e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        } else {
455e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                            for (int i = 0; i < mNumberOfProjections; i++) {
456e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                                mActiveTextDepthMask[i].set(currentNodeDepth, false);
457e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                            }
458e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        }
459e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        currentNodeDepth--;
460e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    }
461e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
462e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                } else if ((eventType == XmlPullParser.TEXT) && (!parser.isWhitespace())) {
463e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    for (int i = 0; i < mNumberOfProjections; i++) {
464e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        if ((currentNodeDepth >= 0) &&
465e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                            (mActiveTextDepthMask[i].get(currentNodeDepth))) {
466e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                            mCurrentValues[i + 1] += parser.getText();
467e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                        }
468e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                    }
469e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                }
470e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne
471e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne                eventType = parser.next();
472e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne            }
473e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne        }
474e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne    }
475e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne}
476