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 * <provider android:name="XmlDocumentProvider" android:authorities="xmldocument" /> 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 * <library> 105e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <book id="EH94"> 106e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <title>The Old Man and the Sea</title> 107e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <author>Ernest Hemingway</author> 108e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * </book> 109e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <book id="XX10"> 110e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <title>The Arabian Nights: Tales of 1,001 Nights</title> 111e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * </book> 112e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <no-id> 113e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <book> 114e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <title>Animal Farm</title> 115e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * <author>George Orwell</author> 116e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * </book> 117e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * </no-id> 118e27049a31a7014edbb4b044f14607a3bbcdf7b61Gilles Debunne * </library> 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