1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.content;
18
19import android.net.Uri;
20import android.util.Xml;
21
22import org.xml.sax.Attributes;
23import org.xml.sax.Locator;
24import org.xml.sax.SAXException;
25
26import java.io.IOException;
27import java.io.InputStream;
28import java.util.Stack;
29
30/**
31 * Inserts default data from InputStream, should be in XML format.
32 * If the provider syncs data to the server, the imported data will be synced to the server.
33 * <p>Samples:</p>
34 * <br/>
35 *  Insert one row:
36 * <pre>
37 * &lt;row uri="content://contacts/people">
38 *  &lt;Col column = "name" value = "foo feebe "/>
39 *  &lt;Col column = "addr" value = "Tx"/>
40 * &lt;/row></pre>
41 * <br/>
42 * Delete, it must be in order of uri, select, and arg:
43 * <pre>
44 * &lt;del uri="content://contacts/people" select="name=? and addr=?"
45 *  arg1 = "foo feebe" arg2 ="Tx"/></pre>
46 * <br/>
47 *  Use first row's uri to insert into another table,
48 *  content://contacts/people/1/phones:
49 * <pre>
50 * &lt;row uri="content://contacts/people">
51 *  &lt;col column = "name" value = "foo feebe"/>
52 *  &lt;col column = "addr" value = "Tx"/>
53 *  &lt;row postfix="phones">
54 *    &lt;col column="number" value="512-514-6535"/>
55 *  &lt;/row>
56 *  &lt;row postfix="phones">
57 *    &lt;col column="cell" value="512-514-6535"/>
58 *  &lt;/row>
59 * &lt;/row></pre>
60 * <br/>
61 *  Insert multiple rows in to same table and same attributes:
62 * <pre>
63 * &lt;row uri="content://contacts/people" >
64 *  &lt;row>
65 *   &lt;col column= "name" value = "foo feebe"/>
66 *   &lt;col column= "addr" value = "Tx"/>
67 *  &lt;/row>
68 *  &lt;row>
69 *  &lt;/row>
70 * &lt;/row></pre>
71 *
72 * @hide
73 */
74public class DefaultDataHandler implements ContentInsertHandler {
75    private final static String ROW = "row";
76    private final static String COL = "col";
77    private final static String URI_STR = "uri";
78    private final static String POSTFIX = "postfix";
79    private final static String DEL = "del";
80    private final static String SELECT = "select";
81    private final static String ARG = "arg";
82
83    private Stack<Uri> mUris = new Stack<Uri>();
84    private ContentValues mValues;
85    private ContentResolver mContentResolver;
86
87    public void insert(ContentResolver contentResolver, InputStream in)
88            throws IOException, SAXException {
89        mContentResolver = contentResolver;
90        Xml.parse(in, Xml.Encoding.UTF_8, this);
91    }
92
93    public void insert(ContentResolver contentResolver, String in)
94        throws SAXException {
95        mContentResolver = contentResolver;
96        Xml.parse(in, this);
97    }
98
99    private void parseRow(Attributes atts) throws SAXException {
100        String uriStr = atts.getValue(URI_STR);
101        Uri uri;
102        if (uriStr != null) {
103            // case 1
104            uri = Uri.parse(uriStr);
105            if (uri == null) {
106                throw new SAXException("attribute " +
107                        atts.getValue(URI_STR) + " parsing failure");
108            }
109
110        } else if (mUris.size() > 0){
111            // case 2
112            String postfix = atts.getValue(POSTFIX);
113            if (postfix != null) {
114                uri = Uri.withAppendedPath(mUris.lastElement(),
115                        postfix);
116            } else {
117                uri = mUris.lastElement();
118            }
119        } else {
120            throw new SAXException("attribute parsing failure");
121        }
122
123        mUris.push(uri);
124
125    }
126
127    private Uri insertRow() {
128        Uri u = mContentResolver.insert(mUris.lastElement(), mValues);
129        mValues = null;
130        return u;
131    }
132
133    public void startElement(String uri, String localName, String name,
134            Attributes atts) throws SAXException {
135        if (ROW.equals(localName)) {
136            if (mValues != null) {
137                // case 2, <Col> before <Row> insert last uri
138                if (mUris.empty()) {
139                    throw new SAXException("uri is empty");
140                }
141                Uri nextUri = insertRow();
142                if (nextUri == null) {
143                    throw new SAXException("insert to uri " +
144                            mUris.lastElement().toString() + " failure");
145                } else {
146                    // make sure the stack lastElement save uri for more than one row
147                    mUris.pop();
148                    mUris.push(nextUri);
149                    parseRow(atts);
150                }
151            } else {
152                int attrLen = atts.getLength();
153                if (attrLen == 0) {
154                    // case 3, share same uri as last level
155                    mUris.push(mUris.lastElement());
156                } else {
157                    parseRow(atts);
158                }
159            }
160        } else if (COL.equals(localName)) {
161            int attrLen = atts.getLength();
162            if (attrLen != 2) {
163                throw new SAXException("illegal attributes number " + attrLen);
164            }
165            String key = atts.getValue(0);
166            String value = atts.getValue(1);
167            if (key != null && key.length() > 0 && value != null && value.length() > 0) {
168                if (mValues == null) {
169                    mValues = new ContentValues();
170                }
171                mValues.put(key, value);
172            } else {
173                throw new SAXException("illegal attributes value");
174            }
175        } else if (DEL.equals(localName)){
176            Uri u = Uri.parse(atts.getValue(URI_STR));
177            if (u == null) {
178                throw new SAXException("attribute " +
179                        atts.getValue(URI_STR) + " parsing failure");
180            }
181            int attrLen = atts.getLength() - 2;
182            if (attrLen > 0) {
183                String[] selectionArgs = new String[attrLen];
184                for (int i = 0; i < attrLen; i++) {
185                    selectionArgs[i] = atts.getValue(i+2);
186                }
187                mContentResolver.delete(u, atts.getValue(1), selectionArgs);
188            } else if (attrLen == 0){
189                mContentResolver.delete(u, atts.getValue(1), null);
190            } else {
191                mContentResolver.delete(u, null, null);
192            }
193
194        } else {
195            throw new SAXException("unknown element: " + localName);
196        }
197    }
198
199    public void endElement(String uri, String localName, String name)
200            throws SAXException {
201        if (ROW.equals(localName)) {
202            if (mUris.empty()) {
203                throw new SAXException("uri mismatch");
204            }
205            if (mValues != null) {
206                insertRow();
207            }
208            mUris.pop();
209        }
210    }
211
212
213    public void characters(char[] ch, int start, int length)
214            throws SAXException {
215        // TODO Auto-generated method stub
216
217    }
218
219    public void endDocument() throws SAXException {
220        // TODO Auto-generated method stub
221
222    }
223
224    public void endPrefixMapping(String prefix) throws SAXException {
225        // TODO Auto-generated method stub
226
227    }
228
229    public void ignorableWhitespace(char[] ch, int start, int length)
230            throws SAXException {
231        // TODO Auto-generated method stub
232
233    }
234
235    public void processingInstruction(String target, String data)
236            throws SAXException {
237        // TODO Auto-generated method stub
238
239    }
240
241    public void setDocumentLocator(Locator locator) {
242        // TODO Auto-generated method stub
243
244    }
245
246    public void skippedEntity(String name) throws SAXException {
247        // TODO Auto-generated method stub
248
249    }
250
251    public void startDocument() throws SAXException {
252        // TODO Auto-generated method stub
253
254    }
255
256    public void startPrefixMapping(String prefix, String uri)
257            throws SAXException {
258        // TODO Auto-generated method stub
259
260    }
261
262}
263