blob: 344d7ef8c83f82bac0fa743b172a6a1e8825b356 [file] [log] [blame]
Tom Taylor087ff0b2009-12-18 10:08:35 -08001/*
2 * Copyright (C) 2006 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
Dianne Hackborn2269d1572010-02-24 19:54:22 -080017package com.android.internal.util;
18
Mathew Inwoodaf972c82018-08-20 14:13:20 +010019import android.annotation.UnsupportedAppUsage;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070020import android.graphics.Bitmap;
21import android.graphics.BitmapFactory;
22import android.graphics.Bitmap.CompressFormat;
23import android.net.Uri;
Svet Ganov7121e182015-07-13 22:38:12 -070024import android.text.TextUtils;
Dianne Hackborna83ce1d2015-03-11 15:16:13 -070025import android.util.ArrayMap;
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070026import android.util.Base64;
Jeff Sharkeyded7b752013-03-22 13:43:41 -070027import android.util.Xml;
Tom Taylor087ff0b2009-12-18 10:08:35 -080028
29import org.xmlpull.v1.XmlPullParser;
30import org.xmlpull.v1.XmlPullParserException;
31import org.xmlpull.v1.XmlSerializer;
32
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -070033import java.io.ByteArrayOutputStream;
Tom Taylor087ff0b2009-12-18 10:08:35 -080034import java.io.IOException;
35import java.io.InputStream;
36import java.io.OutputStream;
Jeff Sharkeyded7b752013-03-22 13:43:41 -070037import java.net.ProtocolException;
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +010038import java.nio.charset.StandardCharsets;
Tom Taylor087ff0b2009-12-18 10:08:35 -080039import java.util.ArrayList;
40import java.util.HashMap;
Adam Powell212db7d2010-04-08 16:24:46 -070041import java.util.HashSet;
Tom Taylor087ff0b2009-12-18 10:08:35 -080042import java.util.Iterator;
43import java.util.List;
44import java.util.Map;
45import java.util.Set;
46
Tom Taylor087ff0b2009-12-18 10:08:35 -080047/** {@hide} */
Jeff Sharkeyded7b752013-03-22 13:43:41 -070048public class XmlUtils {
Tom Taylor087ff0b2009-12-18 10:08:35 -080049
Svet Ganov7121e182015-07-13 22:38:12 -070050 private static final String STRING_ARRAY_SEPARATOR = ":";
51
Mathew Inwoodaf972c82018-08-20 14:13:20 +010052 @UnsupportedAppUsage
Tom Taylor087ff0b2009-12-18 10:08:35 -080053 public static void skipCurrentTag(XmlPullParser parser)
54 throws XmlPullParserException, IOException {
55 int outerDepth = parser.getDepth();
56 int type;
57 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
58 && (type != XmlPullParser.END_TAG
59 || parser.getDepth() > outerDepth)) {
60 }
61 }
62
63 public static final int
64 convertValueToList(CharSequence value, String[] options, int defaultValue)
65 {
Jeff Sharkey9bc99f82018-11-27 13:38:56 -070066 if (!TextUtils.isEmpty(value)) {
Tom Taylor087ff0b2009-12-18 10:08:35 -080067 for (int i = 0; i < options.length; i++) {
68 if (value.equals(options[i]))
69 return i;
70 }
71 }
72
73 return defaultValue;
74 }
75
Mathew Inwoodaf972c82018-08-20 14:13:20 +010076 @UnsupportedAppUsage
Tom Taylor087ff0b2009-12-18 10:08:35 -080077 public static final boolean
78 convertValueToBoolean(CharSequence value, boolean defaultValue)
79 {
80 boolean result = false;
81
Jeff Sharkey9bc99f82018-11-27 13:38:56 -070082 if (TextUtils.isEmpty(value)) {
Tom Taylor087ff0b2009-12-18 10:08:35 -080083 return defaultValue;
Jeff Sharkey9bc99f82018-11-27 13:38:56 -070084 }
Tom Taylor087ff0b2009-12-18 10:08:35 -080085
86 if (value.equals("1")
87 || value.equals("true")
88 || value.equals("TRUE"))
89 result = true;
90
91 return result;
92 }
93
Mathew Inwoodaf972c82018-08-20 14:13:20 +010094 @UnsupportedAppUsage
Tom Taylor087ff0b2009-12-18 10:08:35 -080095 public static final int
96 convertValueToInt(CharSequence charSeq, int defaultValue)
97 {
Jeff Sharkey9bc99f82018-11-27 13:38:56 -070098 if (TextUtils.isEmpty(charSeq)) {
Tom Taylor087ff0b2009-12-18 10:08:35 -080099 return defaultValue;
Jeff Sharkey9bc99f82018-11-27 13:38:56 -0700100 }
Tom Taylor087ff0b2009-12-18 10:08:35 -0800101
102 String nm = charSeq.toString();
103
104 // XXX This code is copied from Integer.decode() so we don't
105 // have to instantiate an Integer!
106
107 int value;
108 int sign = 1;
109 int index = 0;
110 int len = nm.length();
111 int base = 10;
112
113 if ('-' == nm.charAt(0)) {
114 sign = -1;
115 index++;
116 }
117
118 if ('0' == nm.charAt(index)) {
119 // Quick check for a zero by itself
120 if (index == (len - 1))
121 return 0;
122
123 char c = nm.charAt(index + 1);
124
125 if ('x' == c || 'X' == c) {
126 index += 2;
127 base = 16;
128 } else {
129 index++;
130 base = 8;
131 }
132 }
133 else if ('#' == nm.charAt(index))
134 {
135 index++;
136 base = 16;
137 }
138
139 return Integer.parseInt(nm.substring(index), base) * sign;
140 }
141
Romain Guya8f6d5f2012-11-27 11:12:26 -0800142 public static int convertValueToUnsignedInt(String value, int defaultValue) {
Jeff Sharkey9bc99f82018-11-27 13:38:56 -0700143 if (TextUtils.isEmpty(value)) {
Tom Taylor087ff0b2009-12-18 10:08:35 -0800144 return defaultValue;
Romain Guya8f6d5f2012-11-27 11:12:26 -0800145 }
Tom Taylor087ff0b2009-12-18 10:08:35 -0800146
147 return parseUnsignedIntAttribute(value);
148 }
149
Romain Guya8f6d5f2012-11-27 11:12:26 -0800150 public static int parseUnsignedIntAttribute(CharSequence charSeq) {
Tom Taylor087ff0b2009-12-18 10:08:35 -0800151 String value = charSeq.toString();
152
153 long bits;
154 int index = 0;
155 int len = value.length();
156 int base = 10;
157
158 if ('0' == value.charAt(index)) {
159 // Quick check for zero by itself
160 if (index == (len - 1))
161 return 0;
162
163 char c = value.charAt(index + 1);
164
165 if ('x' == c || 'X' == c) { // check for hex
166 index += 2;
167 base = 16;
168 } else { // check for octal
169 index++;
170 base = 8;
171 }
172 } else if ('#' == value.charAt(index)) {
173 index++;
174 base = 16;
175 }
176
177 return (int) Long.parseLong(value.substring(index), base);
178 }
179
180 /**
181 * Flatten a Map into an output stream as XML. The map can later be
182 * read back with readMapXml().
183 *
184 * @param val The map to be flattened.
185 * @param out Where to write the XML data.
186 *
187 * @see #writeMapXml(Map, String, XmlSerializer)
188 * @see #writeListXml
189 * @see #writeValueXml
190 * @see #readMapXml
191 */
Mathew Inwoodaf972c82018-08-20 14:13:20 +0100192 @UnsupportedAppUsage
Tom Taylor087ff0b2009-12-18 10:08:35 -0800193 public static final void writeMapXml(Map val, OutputStream out)
194 throws XmlPullParserException, java.io.IOException {
195 XmlSerializer serializer = new FastXmlSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100196 serializer.setOutput(out, StandardCharsets.UTF_8.name());
Tom Taylor087ff0b2009-12-18 10:08:35 -0800197 serializer.startDocument(null, true);
198 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
199 writeMapXml(val, null, serializer);
200 serializer.endDocument();
201 }
202
203 /**
204 * Flatten a List into an output stream as XML. The list can later be
205 * read back with readListXml().
206 *
207 * @param val The list to be flattened.
208 * @param out Where to write the XML data.
209 *
210 * @see #writeListXml(List, String, XmlSerializer)
211 * @see #writeMapXml
212 * @see #writeValueXml
213 * @see #readListXml
214 */
215 public static final void writeListXml(List val, OutputStream out)
216 throws XmlPullParserException, java.io.IOException
217 {
218 XmlSerializer serializer = Xml.newSerializer();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100219 serializer.setOutput(out, StandardCharsets.UTF_8.name());
Tom Taylor087ff0b2009-12-18 10:08:35 -0800220 serializer.startDocument(null, true);
221 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
222 writeListXml(val, null, serializer);
223 serializer.endDocument();
224 }
225
226 /**
227 * Flatten a Map into an XmlSerializer. The map can later be read back
228 * with readThisMapXml().
229 *
230 * @param val The map to be flattened.
231 * @param name Name attribute to include with this list's tag, or null for
232 * none.
233 * @param out XmlSerializer to write the map into.
234 *
235 * @see #writeMapXml(Map, OutputStream)
236 * @see #writeListXml
237 * @see #writeValueXml
238 * @see #readMapXml
239 */
240 public static final void writeMapXml(Map val, String name, XmlSerializer out)
Craig Mautneref73ee12014-04-23 11:45:37 -0700241 throws XmlPullParserException, java.io.IOException {
242 writeMapXml(val, name, out, null);
243 }
244
245 /**
246 * Flatten a Map into an XmlSerializer. The map can later be read back
247 * with readThisMapXml().
248 *
249 * @param val The map to be flattened.
250 * @param name Name attribute to include with this list's tag, or null for
251 * none.
252 * @param out XmlSerializer to write the map into.
253 * @param callback Method to call when an Object type is not recognized.
254 *
255 * @see #writeMapXml(Map, OutputStream)
256 * @see #writeListXml
257 * @see #writeValueXml
258 * @see #readMapXml
259 *
260 * @hide
261 */
262 public static final void writeMapXml(Map val, String name, XmlSerializer out,
263 WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
264
Tom Taylor087ff0b2009-12-18 10:08:35 -0800265 if (val == null) {
266 out.startTag(null, "null");
267 out.endTag(null, "null");
268 return;
269 }
270
Tom Taylor087ff0b2009-12-18 10:08:35 -0800271 out.startTag(null, "map");
272 if (name != null) {
273 out.attribute(null, "name", name);
274 }
275
Craig Mautneref73ee12014-04-23 11:45:37 -0700276 writeMapXml(val, out, callback);
Tom Taylor087ff0b2009-12-18 10:08:35 -0800277
278 out.endTag(null, "map");
279 }
280
281 /**
Craig Mautneref73ee12014-04-23 11:45:37 -0700282 * Flatten a Map into an XmlSerializer. The map can later be read back
283 * with readThisMapXml(). This method presumes that the start tag and
284 * name attribute have already been written and does not write an end tag.
285 *
286 * @param val The map to be flattened.
287 * @param out XmlSerializer to write the map into.
288 *
289 * @see #writeMapXml(Map, OutputStream)
290 * @see #writeListXml
291 * @see #writeValueXml
292 * @see #readMapXml
293 *
294 * @hide
295 */
296 public static final void writeMapXml(Map val, XmlSerializer out,
297 WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
298 if (val == null) {
299 return;
300 }
301
302 Set s = val.entrySet();
303 Iterator i = s.iterator();
304
305 while (i.hasNext()) {
306 Map.Entry e = (Map.Entry)i.next();
307 writeValueXml(e.getValue(), (String)e.getKey(), out, callback);
308 }
309 }
310
311 /**
Tom Taylor087ff0b2009-12-18 10:08:35 -0800312 * Flatten a List into an XmlSerializer. The list can later be read back
313 * with readThisListXml().
314 *
315 * @param val The list to be flattened.
316 * @param name Name attribute to include with this list's tag, or null for
317 * none.
318 * @param out XmlSerializer to write the list into.
319 *
320 * @see #writeListXml(List, OutputStream)
321 * @see #writeMapXml
322 * @see #writeValueXml
323 * @see #readListXml
324 */
325 public static final void writeListXml(List val, String name, XmlSerializer out)
326 throws XmlPullParserException, java.io.IOException
327 {
328 if (val == null) {
329 out.startTag(null, "null");
330 out.endTag(null, "null");
331 return;
332 }
333
334 out.startTag(null, "list");
335 if (name != null) {
336 out.attribute(null, "name", name);
337 }
338
339 int N = val.size();
340 int i=0;
341 while (i < N) {
342 writeValueXml(val.get(i), null, out);
343 i++;
344 }
345
346 out.endTag(null, "list");
347 }
Adam Powell212db7d2010-04-08 16:24:46 -0700348
349 public static final void writeSetXml(Set val, String name, XmlSerializer out)
350 throws XmlPullParserException, java.io.IOException {
351 if (val == null) {
352 out.startTag(null, "null");
353 out.endTag(null, "null");
354 return;
355 }
356
357 out.startTag(null, "set");
358 if (name != null) {
359 out.attribute(null, "name", name);
360 }
361
362 for (Object v : val) {
363 writeValueXml(v, null, out);
364 }
365
366 out.endTag(null, "set");
367 }
Tom Taylor087ff0b2009-12-18 10:08:35 -0800368
369 /**
370 * Flatten a byte[] into an XmlSerializer. The list can later be read back
371 * with readThisByteArrayXml().
372 *
373 * @param val The byte array to be flattened.
374 * @param name Name attribute to include with this array's tag, or null for
375 * none.
376 * @param out XmlSerializer to write the array into.
377 *
378 * @see #writeMapXml
379 * @see #writeValueXml
380 */
381 public static final void writeByteArrayXml(byte[] val, String name,
382 XmlSerializer out)
383 throws XmlPullParserException, java.io.IOException {
384
385 if (val == null) {
386 out.startTag(null, "null");
387 out.endTag(null, "null");
388 return;
389 }
390
391 out.startTag(null, "byte-array");
392 if (name != null) {
393 out.attribute(null, "name", name);
394 }
395
396 final int N = val.length;
397 out.attribute(null, "num", Integer.toString(N));
398
399 StringBuilder sb = new StringBuilder(val.length*2);
400 for (int i=0; i<N; i++) {
401 int b = val[i];
Roshan Pius651209b2016-05-31 08:39:49 -0700402 int h = (b >> 4) & 0x0f;
403 sb.append((char)(h >= 10 ? ('a'+h-10) : ('0'+h)));
404 h = b & 0x0f;
405 sb.append((char)(h >= 10 ? ('a'+h-10) : ('0'+h)));
Tom Taylor087ff0b2009-12-18 10:08:35 -0800406 }
407
408 out.text(sb.toString());
409
410 out.endTag(null, "byte-array");
411 }
412
413 /**
414 * Flatten an int[] into an XmlSerializer. The list can later be read back
415 * with readThisIntArrayXml().
416 *
417 * @param val The int array to be flattened.
418 * @param name Name attribute to include with this array's tag, or null for
419 * none.
420 * @param out XmlSerializer to write the array into.
421 *
422 * @see #writeMapXml
423 * @see #writeValueXml
424 * @see #readThisIntArrayXml
425 */
426 public static final void writeIntArrayXml(int[] val, String name,
427 XmlSerializer out)
428 throws XmlPullParserException, java.io.IOException {
429
430 if (val == null) {
431 out.startTag(null, "null");
432 out.endTag(null, "null");
433 return;
434 }
435
436 out.startTag(null, "int-array");
437 if (name != null) {
438 out.attribute(null, "name", name);
439 }
440
441 final int N = val.length;
442 out.attribute(null, "num", Integer.toString(N));
443
444 for (int i=0; i<N; i++) {
445 out.startTag(null, "item");
446 out.attribute(null, "value", Integer.toString(val[i]));
447 out.endTag(null, "item");
448 }
449
450 out.endTag(null, "int-array");
451 }
452
453 /**
Craig Mautneref73ee12014-04-23 11:45:37 -0700454 * Flatten a long[] into an XmlSerializer. The list can later be read back
455 * with readThisLongArrayXml().
456 *
457 * @param val The long array to be flattened.
458 * @param name Name attribute to include with this array's tag, or null for
459 * none.
460 * @param out XmlSerializer to write the array into.
461 *
462 * @see #writeMapXml
463 * @see #writeValueXml
464 * @see #readThisIntArrayXml
465 */
466 public static final void writeLongArrayXml(long[] val, String name, XmlSerializer out)
467 throws XmlPullParserException, java.io.IOException {
468
469 if (val == null) {
470 out.startTag(null, "null");
471 out.endTag(null, "null");
472 return;
473 }
474
475 out.startTag(null, "long-array");
476 if (name != null) {
477 out.attribute(null, "name", name);
478 }
479
480 final int N = val.length;
481 out.attribute(null, "num", Integer.toString(N));
482
483 for (int i=0; i<N; i++) {
484 out.startTag(null, "item");
485 out.attribute(null, "value", Long.toString(val[i]));
486 out.endTag(null, "item");
487 }
488
489 out.endTag(null, "long-array");
490 }
491
492 /**
493 * Flatten a double[] into an XmlSerializer. The list can later be read back
494 * with readThisDoubleArrayXml().
495 *
496 * @param val The double array to be flattened.
497 * @param name Name attribute to include with this array's tag, or null for
498 * none.
499 * @param out XmlSerializer to write the array into.
500 *
501 * @see #writeMapXml
502 * @see #writeValueXml
503 * @see #readThisIntArrayXml
504 */
505 public static final void writeDoubleArrayXml(double[] val, String name, XmlSerializer out)
506 throws XmlPullParserException, java.io.IOException {
507
508 if (val == null) {
509 out.startTag(null, "null");
510 out.endTag(null, "null");
511 return;
512 }
513
514 out.startTag(null, "double-array");
515 if (name != null) {
516 out.attribute(null, "name", name);
517 }
518
519 final int N = val.length;
520 out.attribute(null, "num", Integer.toString(N));
521
522 for (int i=0; i<N; i++) {
523 out.startTag(null, "item");
524 out.attribute(null, "value", Double.toString(val[i]));
525 out.endTag(null, "item");
526 }
527
528 out.endTag(null, "double-array");
529 }
530
531 /**
532 * Flatten a String[] into an XmlSerializer. The list can later be read back
533 * with readThisStringArrayXml().
534 *
Craig Mautner73bdf972014-12-09 18:10:20 -0800535 * @param val The String array to be flattened.
Craig Mautneref73ee12014-04-23 11:45:37 -0700536 * @param name Name attribute to include with this array's tag, or null for
537 * none.
538 * @param out XmlSerializer to write the array into.
539 *
540 * @see #writeMapXml
541 * @see #writeValueXml
542 * @see #readThisIntArrayXml
543 */
544 public static final void writeStringArrayXml(String[] val, String name, XmlSerializer out)
545 throws XmlPullParserException, java.io.IOException {
546
547 if (val == null) {
548 out.startTag(null, "null");
549 out.endTag(null, "null");
550 return;
551 }
552
553 out.startTag(null, "string-array");
554 if (name != null) {
555 out.attribute(null, "name", name);
556 }
557
558 final int N = val.length;
559 out.attribute(null, "num", Integer.toString(N));
560
561 for (int i=0; i<N; i++) {
562 out.startTag(null, "item");
563 out.attribute(null, "value", val[i]);
564 out.endTag(null, "item");
565 }
566
567 out.endTag(null, "string-array");
568 }
569
570 /**
Craig Mautner73bdf972014-12-09 18:10:20 -0800571 * Flatten a boolean[] into an XmlSerializer. The list can later be read back
572 * with readThisBooleanArrayXml().
573 *
574 * @param val The boolean array to be flattened.
575 * @param name Name attribute to include with this array's tag, or null for
576 * none.
577 * @param out XmlSerializer to write the array into.
578 *
579 * @see #writeMapXml
580 * @see #writeValueXml
581 * @see #readThisIntArrayXml
582 */
583 public static final void writeBooleanArrayXml(boolean[] val, String name, XmlSerializer out)
584 throws XmlPullParserException, java.io.IOException {
585
586 if (val == null) {
587 out.startTag(null, "null");
588 out.endTag(null, "null");
589 return;
590 }
591
592 out.startTag(null, "boolean-array");
593 if (name != null) {
594 out.attribute(null, "name", name);
595 }
596
597 final int N = val.length;
598 out.attribute(null, "num", Integer.toString(N));
599
600 for (int i=0; i<N; i++) {
601 out.startTag(null, "item");
602 out.attribute(null, "value", Boolean.toString(val[i]));
603 out.endTag(null, "item");
604 }
605
606 out.endTag(null, "boolean-array");
607 }
608
609 /**
Tom Taylor087ff0b2009-12-18 10:08:35 -0800610 * Flatten an object's value into an XmlSerializer. The value can later
611 * be read back with readThisValueXml().
612 *
613 * Currently supported value types are: null, String, Integer, Long,
614 * Float, Double Boolean, Map, List.
615 *
616 * @param v The object to be flattened.
617 * @param name Name attribute to include with this value's tag, or null
618 * for none.
619 * @param out XmlSerializer to write the object into.
620 *
621 * @see #writeMapXml
622 * @see #writeListXml
623 * @see #readValueXml
624 */
625 public static final void writeValueXml(Object v, String name, XmlSerializer out)
Craig Mautneref73ee12014-04-23 11:45:37 -0700626 throws XmlPullParserException, java.io.IOException {
627 writeValueXml(v, name, out, null);
628 }
629
630 /**
631 * Flatten an object's value into an XmlSerializer. The value can later
632 * be read back with readThisValueXml().
633 *
634 * Currently supported value types are: null, String, Integer, Long,
635 * Float, Double Boolean, Map, List.
636 *
637 * @param v The object to be flattened.
638 * @param name Name attribute to include with this value's tag, or null
639 * for none.
640 * @param out XmlSerializer to write the object into.
641 * @param callback Handler for Object types not recognized.
642 *
643 * @see #writeMapXml
644 * @see #writeListXml
645 * @see #readValueXml
646 */
647 private static final void writeValueXml(Object v, String name, XmlSerializer out,
648 WriteMapCallback callback) throws XmlPullParserException, java.io.IOException {
Tom Taylor087ff0b2009-12-18 10:08:35 -0800649 String typeStr;
650 if (v == null) {
651 out.startTag(null, "null");
652 if (name != null) {
653 out.attribute(null, "name", name);
654 }
655 out.endTag(null, "null");
656 return;
657 } else if (v instanceof String) {
658 out.startTag(null, "string");
659 if (name != null) {
660 out.attribute(null, "name", name);
661 }
662 out.text(v.toString());
663 out.endTag(null, "string");
664 return;
665 } else if (v instanceof Integer) {
666 typeStr = "int";
667 } else if (v instanceof Long) {
668 typeStr = "long";
669 } else if (v instanceof Float) {
670 typeStr = "float";
671 } else if (v instanceof Double) {
672 typeStr = "double";
673 } else if (v instanceof Boolean) {
674 typeStr = "boolean";
675 } else if (v instanceof byte[]) {
676 writeByteArrayXml((byte[])v, name, out);
677 return;
678 } else if (v instanceof int[]) {
679 writeIntArrayXml((int[])v, name, out);
680 return;
Craig Mautneref73ee12014-04-23 11:45:37 -0700681 } else if (v instanceof long[]) {
682 writeLongArrayXml((long[])v, name, out);
683 return;
684 } else if (v instanceof double[]) {
685 writeDoubleArrayXml((double[])v, name, out);
686 return;
687 } else if (v instanceof String[]) {
688 writeStringArrayXml((String[])v, name, out);
689 return;
Craig Mautner73bdf972014-12-09 18:10:20 -0800690 } else if (v instanceof boolean[]) {
691 writeBooleanArrayXml((boolean[])v, name, out);
692 return;
Tom Taylor087ff0b2009-12-18 10:08:35 -0800693 } else if (v instanceof Map) {
694 writeMapXml((Map)v, name, out);
695 return;
696 } else if (v instanceof List) {
Craig Mautneref73ee12014-04-23 11:45:37 -0700697 writeListXml((List) v, name, out);
Tom Taylor087ff0b2009-12-18 10:08:35 -0800698 return;
Adam Powell212db7d2010-04-08 16:24:46 -0700699 } else if (v instanceof Set) {
Craig Mautneref73ee12014-04-23 11:45:37 -0700700 writeSetXml((Set) v, name, out);
Adam Powell212db7d2010-04-08 16:24:46 -0700701 return;
Tom Taylor087ff0b2009-12-18 10:08:35 -0800702 } else if (v instanceof CharSequence) {
703 // XXX This is to allow us to at least write something if
704 // we encounter styled text... but it means we will drop all
705 // of the styling information. :(
706 out.startTag(null, "string");
707 if (name != null) {
708 out.attribute(null, "name", name);
709 }
710 out.text(v.toString());
711 out.endTag(null, "string");
712 return;
Craig Mautneref73ee12014-04-23 11:45:37 -0700713 } else if (callback != null) {
714 callback.writeUnknownObject(v, name, out);
715 return;
Tom Taylor087ff0b2009-12-18 10:08:35 -0800716 } else {
717 throw new RuntimeException("writeValueXml: unable to write value " + v);
718 }
719
720 out.startTag(null, typeStr);
721 if (name != null) {
722 out.attribute(null, "name", name);
723 }
724 out.attribute(null, "value", v.toString());
725 out.endTag(null, typeStr);
726 }
727
728 /**
729 * Read a HashMap from an InputStream containing XML. The stream can
730 * previously have been written by writeMapXml().
731 *
732 * @param in The InputStream from which to read.
733 *
734 * @return HashMap The resulting map.
735 *
736 * @see #readListXml
737 * @see #readValueXml
738 * @see #readThisMapXml
739 * #see #writeMapXml
740 */
Narayan Kamathf7482572013-12-18 15:19:17 +0000741 @SuppressWarnings("unchecked")
Mathew Inwoodaf972c82018-08-20 14:13:20 +0100742 @UnsupportedAppUsage
Narayan Kamathf7482572013-12-18 15:19:17 +0000743 public static final HashMap<String, ?> readMapXml(InputStream in)
Tom Taylor087ff0b2009-12-18 10:08:35 -0800744 throws XmlPullParserException, java.io.IOException
745 {
746 XmlPullParser parser = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100747 parser.setInput(in, StandardCharsets.UTF_8.name());
Narayan Kamathf7482572013-12-18 15:19:17 +0000748 return (HashMap<String, ?>) readValueXml(parser, new String[1]);
Tom Taylor087ff0b2009-12-18 10:08:35 -0800749 }
750
751 /**
752 * Read an ArrayList from an InputStream containing XML. The stream can
753 * previously have been written by writeListXml().
754 *
755 * @param in The InputStream from which to read.
756 *
Adam Powell212db7d2010-04-08 16:24:46 -0700757 * @return ArrayList The resulting list.
Tom Taylor087ff0b2009-12-18 10:08:35 -0800758 *
759 * @see #readMapXml
760 * @see #readValueXml
761 * @see #readThisListXml
762 * @see #writeListXml
763 */
764 public static final ArrayList readListXml(InputStream in)
765 throws XmlPullParserException, java.io.IOException
766 {
767 XmlPullParser parser = Xml.newPullParser();
Wojciech Staszkiewicz9e9e2e72015-05-08 14:58:46 +0100768 parser.setInput(in, StandardCharsets.UTF_8.name());
Tom Taylor087ff0b2009-12-18 10:08:35 -0800769 return (ArrayList)readValueXml(parser, new String[1]);
770 }
Adam Powell212db7d2010-04-08 16:24:46 -0700771
772
773 /**
774 * Read a HashSet from an InputStream containing XML. The stream can
775 * previously have been written by writeSetXml().
776 *
777 * @param in The InputStream from which to read.
778 *
779 * @return HashSet The resulting set.
780 *
781 * @throws XmlPullParserException
782 * @throws java.io.IOException
783 *
784 * @see #readValueXml
785 * @see #readThisSetXml
786 * @see #writeSetXml
787 */
788 public static final HashSet readSetXml(InputStream in)
789 throws XmlPullParserException, java.io.IOException {
790 XmlPullParser parser = Xml.newPullParser();
791 parser.setInput(in, null);
792 return (HashSet) readValueXml(parser, new String[1]);
793 }
Tom Taylor087ff0b2009-12-18 10:08:35 -0800794
795 /**
796 * Read a HashMap object from an XmlPullParser. The XML data could
797 * previously have been generated by writeMapXml(). The XmlPullParser
798 * must be positioned <em>after</em> the tag that begins the map.
799 *
800 * @param parser The XmlPullParser from which to read the map data.
801 * @param endTag Name of the tag that will end the map, usually "map".
802 * @param name An array of one string, used to return the name attribute
803 * of the map's tag.
804 *
805 * @return HashMap The newly generated map.
806 *
807 * @see #readMapXml
808 */
Narayan Kamathf7482572013-12-18 15:19:17 +0000809 public static final HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag,
Craig Mautneref73ee12014-04-23 11:45:37 -0700810 String[] name) throws XmlPullParserException, java.io.IOException {
811 return readThisMapXml(parser, endTag, name, null);
812 }
813
814 /**
815 * Read a HashMap object from an XmlPullParser. The XML data could
816 * previously have been generated by writeMapXml(). The XmlPullParser
817 * must be positioned <em>after</em> the tag that begins the map.
818 *
819 * @param parser The XmlPullParser from which to read the map data.
820 * @param endTag Name of the tag that will end the map, usually "map".
821 * @param name An array of one string, used to return the name attribute
822 * of the map's tag.
823 *
824 * @return HashMap The newly generated map.
825 *
826 * @see #readMapXml
827 * @hide
828 */
829 public static final HashMap<String, ?> readThisMapXml(XmlPullParser parser, String endTag,
830 String[] name, ReadMapCallback callback)
831 throws XmlPullParserException, java.io.IOException
Tom Taylor087ff0b2009-12-18 10:08:35 -0800832 {
Narayan Kamathf7482572013-12-18 15:19:17 +0000833 HashMap<String, Object> map = new HashMap<String, Object>();
Tom Taylor087ff0b2009-12-18 10:08:35 -0800834
835 int eventType = parser.getEventType();
836 do {
837 if (eventType == parser.START_TAG) {
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700838 Object val = readThisValueXml(parser, name, callback, false);
839 map.put(name[0], val);
840 } else if (eventType == parser.END_TAG) {
841 if (parser.getName().equals(endTag)) {
842 return map;
843 }
844 throw new XmlPullParserException(
845 "Expected " + endTag + " end tag at: " + parser.getName());
846 }
847 eventType = parser.next();
848 } while (eventType != parser.END_DOCUMENT);
849
850 throw new XmlPullParserException(
851 "Document ended before " + endTag + " end tag");
852 }
853
854 /**
855 * Like {@link #readThisMapXml}, but returns an ArrayMap instead of HashMap.
856 * @hide
857 */
858 public static final ArrayMap<String, ?> readThisArrayMapXml(XmlPullParser parser, String endTag,
859 String[] name, ReadMapCallback callback)
860 throws XmlPullParserException, java.io.IOException
861 {
862 ArrayMap<String, Object> map = new ArrayMap<>();
863
864 int eventType = parser.getEventType();
865 do {
866 if (eventType == parser.START_TAG) {
867 Object val = readThisValueXml(parser, name, callback, true);
Narayan Kamathf7482572013-12-18 15:19:17 +0000868 map.put(name[0], val);
Tom Taylor087ff0b2009-12-18 10:08:35 -0800869 } else if (eventType == parser.END_TAG) {
870 if (parser.getName().equals(endTag)) {
871 return map;
872 }
873 throw new XmlPullParserException(
874 "Expected " + endTag + " end tag at: " + parser.getName());
875 }
876 eventType = parser.next();
877 } while (eventType != parser.END_DOCUMENT);
878
879 throw new XmlPullParserException(
880 "Document ended before " + endTag + " end tag");
881 }
882
883 /**
884 * Read an ArrayList object from an XmlPullParser. The XML data could
885 * previously have been generated by writeListXml(). The XmlPullParser
886 * must be positioned <em>after</em> the tag that begins the list.
887 *
888 * @param parser The XmlPullParser from which to read the list data.
889 * @param endTag Name of the tag that will end the list, usually "list".
890 * @param name An array of one string, used to return the name attribute
891 * of the list's tag.
892 *
893 * @return HashMap The newly generated list.
894 *
895 * @see #readListXml
896 */
Craig Mautneref73ee12014-04-23 11:45:37 -0700897 public static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
898 String[] name) throws XmlPullParserException, java.io.IOException {
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700899 return readThisListXml(parser, endTag, name, null, false);
Craig Mautneref73ee12014-04-23 11:45:37 -0700900 }
901
902 /**
903 * Read an ArrayList object from an XmlPullParser. The XML data could
904 * previously have been generated by writeListXml(). The XmlPullParser
905 * must be positioned <em>after</em> the tag that begins the list.
906 *
907 * @param parser The XmlPullParser from which to read the list data.
908 * @param endTag Name of the tag that will end the list, usually "list".
909 * @param name An array of one string, used to return the name attribute
910 * of the list's tag.
911 *
912 * @return HashMap The newly generated list.
913 *
914 * @see #readListXml
915 */
916 private static final ArrayList readThisListXml(XmlPullParser parser, String endTag,
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700917 String[] name, ReadMapCallback callback, boolean arrayMap)
Craig Mautneref73ee12014-04-23 11:45:37 -0700918 throws XmlPullParserException, java.io.IOException {
Tom Taylor087ff0b2009-12-18 10:08:35 -0800919 ArrayList list = new ArrayList();
920
921 int eventType = parser.getEventType();
922 do {
923 if (eventType == parser.START_TAG) {
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700924 Object val = readThisValueXml(parser, name, callback, arrayMap);
Tom Taylor087ff0b2009-12-18 10:08:35 -0800925 list.add(val);
926 //System.out.println("Adding to list: " + val);
927 } else if (eventType == parser.END_TAG) {
928 if (parser.getName().equals(endTag)) {
929 return list;
930 }
931 throw new XmlPullParserException(
932 "Expected " + endTag + " end tag at: " + parser.getName());
933 }
934 eventType = parser.next();
935 } while (eventType != parser.END_DOCUMENT);
936
937 throw new XmlPullParserException(
938 "Document ended before " + endTag + " end tag");
939 }
Craig Mautneref73ee12014-04-23 11:45:37 -0700940
941 /**
942 * Read a HashSet object from an XmlPullParser. The XML data could previously
943 * have been generated by writeSetXml(). The XmlPullParser must be positioned
944 * <em>after</em> the tag that begins the set.
945 *
946 * @param parser The XmlPullParser from which to read the set data.
947 * @param endTag Name of the tag that will end the set, usually "set".
948 * @param name An array of one string, used to return the name attribute
949 * of the set's tag.
950 *
951 * @return HashSet The newly generated set.
952 *
953 * @throws XmlPullParserException
954 * @throws java.io.IOException
955 *
956 * @see #readSetXml
957 */
958 public static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name)
959 throws XmlPullParserException, java.io.IOException {
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700960 return readThisSetXml(parser, endTag, name, null, false);
Craig Mautneref73ee12014-04-23 11:45:37 -0700961 }
962
Adam Powell212db7d2010-04-08 16:24:46 -0700963 /**
964 * Read a HashSet object from an XmlPullParser. The XML data could previously
965 * have been generated by writeSetXml(). The XmlPullParser must be positioned
966 * <em>after</em> the tag that begins the set.
967 *
968 * @param parser The XmlPullParser from which to read the set data.
969 * @param endTag Name of the tag that will end the set, usually "set".
970 * @param name An array of one string, used to return the name attribute
971 * of the set's tag.
972 *
973 * @return HashSet The newly generated set.
974 *
975 * @throws XmlPullParserException
976 * @throws java.io.IOException
977 *
978 * @see #readSetXml
Craig Mautneref73ee12014-04-23 11:45:37 -0700979 * @hide
Adam Powell212db7d2010-04-08 16:24:46 -0700980 */
Craig Mautneref73ee12014-04-23 11:45:37 -0700981 private static final HashSet readThisSetXml(XmlPullParser parser, String endTag, String[] name,
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700982 ReadMapCallback callback, boolean arrayMap)
983 throws XmlPullParserException, java.io.IOException {
Adam Powell212db7d2010-04-08 16:24:46 -0700984 HashSet set = new HashSet();
985
986 int eventType = parser.getEventType();
987 do {
988 if (eventType == parser.START_TAG) {
Dianne Hackborna83ce1d2015-03-11 15:16:13 -0700989 Object val = readThisValueXml(parser, name, callback, arrayMap);
Adam Powell212db7d2010-04-08 16:24:46 -0700990 set.add(val);
991 //System.out.println("Adding to set: " + val);
992 } else if (eventType == parser.END_TAG) {
993 if (parser.getName().equals(endTag)) {
994 return set;
995 }
996 throw new XmlPullParserException(
997 "Expected " + endTag + " end tag at: " + parser.getName());
998 }
999 eventType = parser.next();
1000 } while (eventType != parser.END_DOCUMENT);
1001
1002 throw new XmlPullParserException(
1003 "Document ended before " + endTag + " end tag");
1004 }
Tom Taylor087ff0b2009-12-18 10:08:35 -08001005
1006 /**
Roshan Pius651209b2016-05-31 08:39:49 -07001007 * Read a byte[] object from an XmlPullParser. The XML data could
1008 * previously have been generated by writeByteArrayXml(). The XmlPullParser
1009 * must be positioned <em>after</em> the tag that begins the list.
1010 *
1011 * @param parser The XmlPullParser from which to read the list data.
1012 * @param endTag Name of the tag that will end the list, usually "list".
1013 * @param name An array of one string, used to return the name attribute
1014 * of the list's tag.
1015 *
1016 * @return Returns a newly generated byte[].
1017 *
1018 * @see #writeByteArrayXml
1019 */
1020 public static final byte[] readThisByteArrayXml(XmlPullParser parser,
1021 String endTag, String[] name)
1022 throws XmlPullParserException, java.io.IOException {
1023
1024 int num;
1025 try {
1026 num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1027 } catch (NullPointerException e) {
1028 throw new XmlPullParserException(
1029 "Need num attribute in byte-array");
1030 } catch (NumberFormatException e) {
1031 throw new XmlPullParserException(
1032 "Not a number in num attribute in byte-array");
1033 }
1034
1035 byte[] array = new byte[num];
1036
1037 int eventType = parser.getEventType();
1038 do {
1039 if (eventType == parser.TEXT) {
1040 if (num > 0) {
1041 String values = parser.getText();
1042 if (values == null || values.length() != num * 2) {
1043 throw new XmlPullParserException(
1044 "Invalid value found in byte-array: " + values);
1045 }
1046 // This is ugly, but keeping it to mirror the logic in #writeByteArrayXml.
1047 for (int i = 0; i < num; i ++) {
1048 char nibbleHighChar = values.charAt(2 * i);
1049 char nibbleLowChar = values.charAt(2 * i + 1);
1050 int nibbleHigh = nibbleHighChar > 'a' ? (nibbleHighChar - 'a' + 10)
1051 : (nibbleHighChar - '0');
1052 int nibbleLow = nibbleLowChar > 'a' ? (nibbleLowChar - 'a' + 10)
1053 : (nibbleLowChar - '0');
1054 array[i] = (byte) ((nibbleHigh & 0x0F) << 4 | (nibbleLow & 0x0F));
1055 }
1056 }
1057 } else if (eventType == parser.END_TAG) {
1058 if (parser.getName().equals(endTag)) {
1059 return array;
1060 } else {
1061 throw new XmlPullParserException(
1062 "Expected " + endTag + " end tag at: "
1063 + parser.getName());
1064 }
1065 }
1066 eventType = parser.next();
1067 } while (eventType != parser.END_DOCUMENT);
1068
1069 throw new XmlPullParserException(
1070 "Document ended before " + endTag + " end tag");
1071 }
1072
1073 /**
Tom Taylor087ff0b2009-12-18 10:08:35 -08001074 * Read an int[] object from an XmlPullParser. The XML data could
1075 * previously have been generated by writeIntArrayXml(). The XmlPullParser
1076 * must be positioned <em>after</em> the tag that begins the list.
1077 *
1078 * @param parser The XmlPullParser from which to read the list data.
1079 * @param endTag Name of the tag that will end the list, usually "list".
1080 * @param name An array of one string, used to return the name attribute
1081 * of the list's tag.
1082 *
1083 * @return Returns a newly generated int[].
1084 *
1085 * @see #readListXml
1086 */
1087 public static final int[] readThisIntArrayXml(XmlPullParser parser,
1088 String endTag, String[] name)
1089 throws XmlPullParserException, java.io.IOException {
1090
1091 int num;
1092 try {
1093 num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1094 } catch (NullPointerException e) {
1095 throw new XmlPullParserException(
Roshan Pius651209b2016-05-31 08:39:49 -07001096 "Need num attribute in int-array");
Tom Taylor087ff0b2009-12-18 10:08:35 -08001097 } catch (NumberFormatException e) {
1098 throw new XmlPullParserException(
Roshan Pius651209b2016-05-31 08:39:49 -07001099 "Not a number in num attribute in int-array");
Tom Taylor087ff0b2009-12-18 10:08:35 -08001100 }
Craig Mautneref73ee12014-04-23 11:45:37 -07001101 parser.next();
Tom Taylor087ff0b2009-12-18 10:08:35 -08001102
1103 int[] array = new int[num];
1104 int i = 0;
1105
1106 int eventType = parser.getEventType();
1107 do {
1108 if (eventType == parser.START_TAG) {
1109 if (parser.getName().equals("item")) {
1110 try {
1111 array[i] = Integer.parseInt(
1112 parser.getAttributeValue(null, "value"));
1113 } catch (NullPointerException e) {
1114 throw new XmlPullParserException(
1115 "Need value attribute in item");
1116 } catch (NumberFormatException e) {
1117 throw new XmlPullParserException(
1118 "Not a number in value attribute in item");
1119 }
1120 } else {
1121 throw new XmlPullParserException(
1122 "Expected item tag at: " + parser.getName());
1123 }
1124 } else if (eventType == parser.END_TAG) {
1125 if (parser.getName().equals(endTag)) {
1126 return array;
1127 } else if (parser.getName().equals("item")) {
1128 i++;
1129 } else {
1130 throw new XmlPullParserException(
1131 "Expected " + endTag + " end tag at: "
1132 + parser.getName());
1133 }
1134 }
1135 eventType = parser.next();
1136 } while (eventType != parser.END_DOCUMENT);
1137
1138 throw new XmlPullParserException(
1139 "Document ended before " + endTag + " end tag");
1140 }
1141
1142 /**
Craig Mautneref73ee12014-04-23 11:45:37 -07001143 * Read a long[] object from an XmlPullParser. The XML data could
1144 * previously have been generated by writeLongArrayXml(). The XmlPullParser
1145 * must be positioned <em>after</em> the tag that begins the list.
1146 *
1147 * @param parser The XmlPullParser from which to read the list data.
1148 * @param endTag Name of the tag that will end the list, usually "list".
1149 * @param name An array of one string, used to return the name attribute
1150 * of the list's tag.
1151 *
1152 * @return Returns a newly generated long[].
1153 *
1154 * @see #readListXml
1155 */
1156 public static final long[] readThisLongArrayXml(XmlPullParser parser,
1157 String endTag, String[] name)
1158 throws XmlPullParserException, java.io.IOException {
1159
1160 int num;
1161 try {
1162 num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1163 } catch (NullPointerException e) {
1164 throw new XmlPullParserException("Need num attribute in long-array");
1165 } catch (NumberFormatException e) {
1166 throw new XmlPullParserException("Not a number in num attribute in long-array");
1167 }
1168 parser.next();
1169
1170 long[] array = new long[num];
1171 int i = 0;
1172
1173 int eventType = parser.getEventType();
1174 do {
1175 if (eventType == parser.START_TAG) {
1176 if (parser.getName().equals("item")) {
1177 try {
1178 array[i] = Long.parseLong(parser.getAttributeValue(null, "value"));
1179 } catch (NullPointerException e) {
1180 throw new XmlPullParserException("Need value attribute in item");
1181 } catch (NumberFormatException e) {
1182 throw new XmlPullParserException("Not a number in value attribute in item");
1183 }
1184 } else {
1185 throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1186 }
1187 } else if (eventType == parser.END_TAG) {
1188 if (parser.getName().equals(endTag)) {
1189 return array;
1190 } else if (parser.getName().equals("item")) {
1191 i++;
1192 } else {
1193 throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1194 parser.getName());
1195 }
1196 }
1197 eventType = parser.next();
1198 } while (eventType != parser.END_DOCUMENT);
1199
1200 throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1201 }
1202
1203 /**
1204 * Read a double[] object from an XmlPullParser. The XML data could
1205 * previously have been generated by writeDoubleArrayXml(). The XmlPullParser
1206 * must be positioned <em>after</em> the tag that begins the list.
1207 *
1208 * @param parser The XmlPullParser from which to read the list data.
1209 * @param endTag Name of the tag that will end the list, usually "double-array".
1210 * @param name An array of one string, used to return the name attribute
1211 * of the list's tag.
1212 *
1213 * @return Returns a newly generated double[].
1214 *
1215 * @see #readListXml
1216 */
1217 public static final double[] readThisDoubleArrayXml(XmlPullParser parser, String endTag,
1218 String[] name) throws XmlPullParserException, java.io.IOException {
1219
1220 int num;
1221 try {
1222 num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1223 } catch (NullPointerException e) {
1224 throw new XmlPullParserException("Need num attribute in double-array");
1225 } catch (NumberFormatException e) {
1226 throw new XmlPullParserException("Not a number in num attribute in double-array");
1227 }
1228 parser.next();
1229
1230 double[] array = new double[num];
1231 int i = 0;
1232
1233 int eventType = parser.getEventType();
1234 do {
1235 if (eventType == parser.START_TAG) {
1236 if (parser.getName().equals("item")) {
1237 try {
1238 array[i] = Double.parseDouble(parser.getAttributeValue(null, "value"));
1239 } catch (NullPointerException e) {
1240 throw new XmlPullParserException("Need value attribute in item");
1241 } catch (NumberFormatException e) {
1242 throw new XmlPullParserException("Not a number in value attribute in item");
1243 }
1244 } else {
1245 throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1246 }
1247 } else if (eventType == parser.END_TAG) {
1248 if (parser.getName().equals(endTag)) {
1249 return array;
1250 } else if (parser.getName().equals("item")) {
1251 i++;
1252 } else {
1253 throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1254 parser.getName());
1255 }
1256 }
1257 eventType = parser.next();
1258 } while (eventType != parser.END_DOCUMENT);
1259
1260 throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1261 }
1262
1263 /**
1264 * Read a String[] object from an XmlPullParser. The XML data could
1265 * previously have been generated by writeStringArrayXml(). The XmlPullParser
1266 * must be positioned <em>after</em> the tag that begins the list.
1267 *
1268 * @param parser The XmlPullParser from which to read the list data.
1269 * @param endTag Name of the tag that will end the list, usually "string-array".
1270 * @param name An array of one string, used to return the name attribute
1271 * of the list's tag.
1272 *
1273 * @return Returns a newly generated String[].
1274 *
1275 * @see #readListXml
1276 */
1277 public static final String[] readThisStringArrayXml(XmlPullParser parser, String endTag,
1278 String[] name) throws XmlPullParserException, java.io.IOException {
1279
1280 int num;
1281 try {
1282 num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1283 } catch (NullPointerException e) {
1284 throw new XmlPullParserException("Need num attribute in string-array");
1285 } catch (NumberFormatException e) {
1286 throw new XmlPullParserException("Not a number in num attribute in string-array");
1287 }
1288 parser.next();
1289
1290 String[] array = new String[num];
1291 int i = 0;
1292
1293 int eventType = parser.getEventType();
1294 do {
1295 if (eventType == parser.START_TAG) {
1296 if (parser.getName().equals("item")) {
1297 try {
1298 array[i] = parser.getAttributeValue(null, "value");
1299 } catch (NullPointerException e) {
1300 throw new XmlPullParserException("Need value attribute in item");
1301 } catch (NumberFormatException e) {
1302 throw new XmlPullParserException("Not a number in value attribute in item");
1303 }
1304 } else {
1305 throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1306 }
1307 } else if (eventType == parser.END_TAG) {
1308 if (parser.getName().equals(endTag)) {
1309 return array;
1310 } else if (parser.getName().equals("item")) {
1311 i++;
1312 } else {
1313 throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1314 parser.getName());
1315 }
1316 }
1317 eventType = parser.next();
1318 } while (eventType != parser.END_DOCUMENT);
1319
1320 throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1321 }
1322
1323 /**
Craig Mautner73bdf972014-12-09 18:10:20 -08001324 * Read a boolean[] object from an XmlPullParser. The XML data could
1325 * previously have been generated by writeBooleanArrayXml(). The XmlPullParser
1326 * must be positioned <em>after</em> the tag that begins the list.
1327 *
1328 * @param parser The XmlPullParser from which to read the list data.
1329 * @param endTag Name of the tag that will end the list, usually "string-array".
1330 * @param name An array of one string, used to return the name attribute
1331 * of the list's tag.
1332 *
1333 * @return Returns a newly generated boolean[].
1334 *
1335 * @see #readListXml
1336 */
1337 public static final boolean[] readThisBooleanArrayXml(XmlPullParser parser, String endTag,
1338 String[] name) throws XmlPullParserException, java.io.IOException {
1339
1340 int num;
1341 try {
1342 num = Integer.parseInt(parser.getAttributeValue(null, "num"));
1343 } catch (NullPointerException e) {
1344 throw new XmlPullParserException("Need num attribute in string-array");
1345 } catch (NumberFormatException e) {
1346 throw new XmlPullParserException("Not a number in num attribute in string-array");
1347 }
1348 parser.next();
1349
1350 boolean[] array = new boolean[num];
1351 int i = 0;
1352
1353 int eventType = parser.getEventType();
1354 do {
1355 if (eventType == parser.START_TAG) {
1356 if (parser.getName().equals("item")) {
1357 try {
Tobias Thiererb0800dc2016-04-21 17:51:41 +01001358 array[i] = Boolean.parseBoolean(parser.getAttributeValue(null, "value"));
Craig Mautner73bdf972014-12-09 18:10:20 -08001359 } catch (NullPointerException e) {
1360 throw new XmlPullParserException("Need value attribute in item");
1361 } catch (NumberFormatException e) {
1362 throw new XmlPullParserException("Not a number in value attribute in item");
1363 }
1364 } else {
1365 throw new XmlPullParserException("Expected item tag at: " + parser.getName());
1366 }
1367 } else if (eventType == parser.END_TAG) {
1368 if (parser.getName().equals(endTag)) {
1369 return array;
1370 } else if (parser.getName().equals("item")) {
1371 i++;
1372 } else {
1373 throw new XmlPullParserException("Expected " + endTag + " end tag at: " +
1374 parser.getName());
1375 }
1376 }
1377 eventType = parser.next();
1378 } while (eventType != parser.END_DOCUMENT);
1379
1380 throw new XmlPullParserException("Document ended before " + endTag + " end tag");
1381 }
1382
1383 /**
Tom Taylor087ff0b2009-12-18 10:08:35 -08001384 * Read a flattened object from an XmlPullParser. The XML data could
1385 * previously have been written with writeMapXml(), writeListXml(), or
1386 * writeValueXml(). The XmlPullParser must be positioned <em>at</em> the
1387 * tag that defines the value.
1388 *
1389 * @param parser The XmlPullParser from which to read the object.
1390 * @param name An array of one string, used to return the name attribute
1391 * of the value's tag.
1392 *
1393 * @return Object The newly generated value object.
1394 *
1395 * @see #readMapXml
1396 * @see #readListXml
1397 * @see #writeValueXml
1398 */
1399 public static final Object readValueXml(XmlPullParser parser, String[] name)
1400 throws XmlPullParserException, java.io.IOException
1401 {
1402 int eventType = parser.getEventType();
1403 do {
1404 if (eventType == parser.START_TAG) {
Dianne Hackborna83ce1d2015-03-11 15:16:13 -07001405 return readThisValueXml(parser, name, null, false);
Tom Taylor087ff0b2009-12-18 10:08:35 -08001406 } else if (eventType == parser.END_TAG) {
1407 throw new XmlPullParserException(
1408 "Unexpected end tag at: " + parser.getName());
1409 } else if (eventType == parser.TEXT) {
1410 throw new XmlPullParserException(
1411 "Unexpected text: " + parser.getText());
1412 }
1413 eventType = parser.next();
1414 } while (eventType != parser.END_DOCUMENT);
1415
1416 throw new XmlPullParserException(
1417 "Unexpected end of document");
1418 }
1419
Craig Mautneref73ee12014-04-23 11:45:37 -07001420 private static final Object readThisValueXml(XmlPullParser parser, String[] name,
Dianne Hackborna83ce1d2015-03-11 15:16:13 -07001421 ReadMapCallback callback, boolean arrayMap)
1422 throws XmlPullParserException, java.io.IOException {
Tom Taylor087ff0b2009-12-18 10:08:35 -08001423 final String valueName = parser.getAttributeValue(null, "name");
1424 final String tagName = parser.getName();
1425
1426 //System.out.println("Reading this value tag: " + tagName + ", name=" + valueName);
1427
1428 Object res;
1429
1430 if (tagName.equals("null")) {
1431 res = null;
1432 } else if (tagName.equals("string")) {
1433 String value = "";
1434 int eventType;
1435 while ((eventType = parser.next()) != parser.END_DOCUMENT) {
1436 if (eventType == parser.END_TAG) {
1437 if (parser.getName().equals("string")) {
1438 name[0] = valueName;
1439 //System.out.println("Returning value for " + valueName + ": " + value);
1440 return value;
1441 }
1442 throw new XmlPullParserException(
1443 "Unexpected end tag in <string>: " + parser.getName());
1444 } else if (eventType == parser.TEXT) {
1445 value += parser.getText();
1446 } else if (eventType == parser.START_TAG) {
1447 throw new XmlPullParserException(
1448 "Unexpected start tag in <string>: " + parser.getName());
1449 }
1450 }
1451 throw new XmlPullParserException(
1452 "Unexpected end of document in <string>");
Vladimir Marko2ddce322013-07-01 14:43:55 +01001453 } else if ((res = readThisPrimitiveValueXml(parser, tagName)) != null) {
1454 // all work already done by readThisPrimitiveValueXml
Roshan Pius651209b2016-05-31 08:39:49 -07001455 } else if (tagName.equals("byte-array")) {
1456 res = readThisByteArrayXml(parser, "byte-array", name);
1457 name[0] = valueName;
1458 //System.out.println("Returning value for " + valueName + ": " + res);
1459 return res;
Tom Taylor087ff0b2009-12-18 10:08:35 -08001460 } else if (tagName.equals("int-array")) {
Tom Taylor087ff0b2009-12-18 10:08:35 -08001461 res = readThisIntArrayXml(parser, "int-array", name);
1462 name[0] = valueName;
1463 //System.out.println("Returning value for " + valueName + ": " + res);
1464 return res;
Craig Mautneref73ee12014-04-23 11:45:37 -07001465 } else if (tagName.equals("long-array")) {
1466 res = readThisLongArrayXml(parser, "long-array", name);
1467 name[0] = valueName;
1468 //System.out.println("Returning value for " + valueName + ": " + res);
1469 return res;
1470 } else if (tagName.equals("double-array")) {
1471 res = readThisDoubleArrayXml(parser, "double-array", name);
1472 name[0] = valueName;
1473 //System.out.println("Returning value for " + valueName + ": " + res);
1474 return res;
1475 } else if (tagName.equals("string-array")) {
1476 res = readThisStringArrayXml(parser, "string-array", name);
1477 name[0] = valueName;
1478 //System.out.println("Returning value for " + valueName + ": " + res);
1479 return res;
Craig Mautner73bdf972014-12-09 18:10:20 -08001480 } else if (tagName.equals("boolean-array")) {
1481 res = readThisBooleanArrayXml(parser, "boolean-array", name);
1482 name[0] = valueName;
1483 //System.out.println("Returning value for " + valueName + ": " + res);
1484 return res;
Tom Taylor087ff0b2009-12-18 10:08:35 -08001485 } else if (tagName.equals("map")) {
1486 parser.next();
Dianne Hackborna83ce1d2015-03-11 15:16:13 -07001487 res = arrayMap
1488 ? readThisArrayMapXml(parser, "map", name, callback)
1489 : readThisMapXml(parser, "map", name, callback);
Tom Taylor087ff0b2009-12-18 10:08:35 -08001490 name[0] = valueName;
1491 //System.out.println("Returning value for " + valueName + ": " + res);
1492 return res;
1493 } else if (tagName.equals("list")) {
1494 parser.next();
Dianne Hackborna83ce1d2015-03-11 15:16:13 -07001495 res = readThisListXml(parser, "list", name, callback, arrayMap);
Tom Taylor087ff0b2009-12-18 10:08:35 -08001496 name[0] = valueName;
1497 //System.out.println("Returning value for " + valueName + ": " + res);
1498 return res;
Adam Powell212db7d2010-04-08 16:24:46 -07001499 } else if (tagName.equals("set")) {
1500 parser.next();
Dianne Hackborna83ce1d2015-03-11 15:16:13 -07001501 res = readThisSetXml(parser, "set", name, callback, arrayMap);
Adam Powell212db7d2010-04-08 16:24:46 -07001502 name[0] = valueName;
1503 //System.out.println("Returning value for " + valueName + ": " + res);
1504 return res;
Craig Mautneref73ee12014-04-23 11:45:37 -07001505 } else if (callback != null) {
1506 res = callback.readThisUnknownObjectXml(parser, tagName);
1507 name[0] = valueName;
1508 return res;
Tom Taylor087ff0b2009-12-18 10:08:35 -08001509 } else {
Craig Mautneref73ee12014-04-23 11:45:37 -07001510 throw new XmlPullParserException("Unknown tag: " + tagName);
Tom Taylor087ff0b2009-12-18 10:08:35 -08001511 }
1512
1513 // Skip through to end tag.
1514 int eventType;
1515 while ((eventType = parser.next()) != parser.END_DOCUMENT) {
1516 if (eventType == parser.END_TAG) {
1517 if (parser.getName().equals(tagName)) {
1518 name[0] = valueName;
1519 //System.out.println("Returning value for " + valueName + ": " + res);
1520 return res;
1521 }
1522 throw new XmlPullParserException(
1523 "Unexpected end tag in <" + tagName + ">: " + parser.getName());
1524 } else if (eventType == parser.TEXT) {
1525 throw new XmlPullParserException(
1526 "Unexpected text in <" + tagName + ">: " + parser.getName());
1527 } else if (eventType == parser.START_TAG) {
1528 throw new XmlPullParserException(
1529 "Unexpected start tag in <" + tagName + ">: " + parser.getName());
1530 }
1531 }
1532 throw new XmlPullParserException(
1533 "Unexpected end of document in <" + tagName + ">");
1534 }
1535
Vladimir Marko2ddce322013-07-01 14:43:55 +01001536 private static final Object readThisPrimitiveValueXml(XmlPullParser parser, String tagName)
1537 throws XmlPullParserException, java.io.IOException
1538 {
1539 try {
1540 if (tagName.equals("int")) {
1541 return Integer.parseInt(parser.getAttributeValue(null, "value"));
1542 } else if (tagName.equals("long")) {
1543 return Long.valueOf(parser.getAttributeValue(null, "value"));
1544 } else if (tagName.equals("float")) {
1545 return new Float(parser.getAttributeValue(null, "value"));
1546 } else if (tagName.equals("double")) {
1547 return new Double(parser.getAttributeValue(null, "value"));
1548 } else if (tagName.equals("boolean")) {
1549 return Boolean.valueOf(parser.getAttributeValue(null, "value"));
1550 } else {
1551 return null;
1552 }
1553 } catch (NullPointerException e) {
1554 throw new XmlPullParserException("Need value attribute in <" + tagName + ">");
1555 } catch (NumberFormatException e) {
1556 throw new XmlPullParserException(
1557 "Not a number in value attribute in <" + tagName + ">");
1558 }
1559 }
1560
Mathew Inwoodaf972c82018-08-20 14:13:20 +01001561 @UnsupportedAppUsage
Tom Taylor087ff0b2009-12-18 10:08:35 -08001562 public static final void beginDocument(XmlPullParser parser, String firstElementName) throws XmlPullParserException, IOException
1563 {
1564 int type;
1565 while ((type=parser.next()) != parser.START_TAG
1566 && type != parser.END_DOCUMENT) {
1567 ;
1568 }
1569
1570 if (type != parser.START_TAG) {
1571 throw new XmlPullParserException("No start tag found");
1572 }
1573
1574 if (!parser.getName().equals(firstElementName)) {
1575 throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
1576 ", expected " + firstElementName);
1577 }
1578 }
1579
Mathew Inwoodaf972c82018-08-20 14:13:20 +01001580 @UnsupportedAppUsage
Tom Taylor087ff0b2009-12-18 10:08:35 -08001581 public static final void nextElement(XmlPullParser parser) throws XmlPullParserException, IOException
1582 {
1583 int type;
1584 while ((type=parser.next()) != parser.START_TAG
1585 && type != parser.END_DOCUMENT) {
1586 ;
1587 }
1588 }
Jeff Browna3bc5652012-04-17 11:42:25 -07001589
1590 public static boolean nextElementWithin(XmlPullParser parser, int outerDepth)
1591 throws IOException, XmlPullParserException {
1592 for (;;) {
1593 int type = parser.next();
1594 if (type == XmlPullParser.END_DOCUMENT
1595 || (type == XmlPullParser.END_TAG && parser.getDepth() == outerDepth)) {
1596 return false;
1597 }
1598 if (type == XmlPullParser.START_TAG
1599 && parser.getDepth() == outerDepth + 1) {
1600 return true;
1601 }
1602 }
1603 }
Jeff Sharkeyded7b752013-03-22 13:43:41 -07001604
Nicolas Prevotd85fc722014-04-16 19:52:08 +01001605 public static int readIntAttribute(XmlPullParser in, String name, int defaultValue) {
1606 final String value = in.getAttributeValue(null, name);
Fyodor Kupolov9f9bf952016-11-01 11:31:32 -07001607 if (TextUtils.isEmpty(value)) {
1608 return defaultValue;
1609 }
Nicolas Prevotd85fc722014-04-16 19:52:08 +01001610 try {
1611 return Integer.parseInt(value);
1612 } catch (NumberFormatException e) {
1613 return defaultValue;
1614 }
1615 }
1616
Jeff Sharkeyded7b752013-03-22 13:43:41 -07001617 public static int readIntAttribute(XmlPullParser in, String name) throws IOException {
1618 final String value = in.getAttributeValue(null, name);
1619 try {
1620 return Integer.parseInt(value);
1621 } catch (NumberFormatException e) {
1622 throw new ProtocolException("problem parsing " + name + "=" + value + " as int");
1623 }
1624 }
1625
1626 public static void writeIntAttribute(XmlSerializer out, String name, int value)
1627 throws IOException {
1628 out.attribute(null, name, Integer.toString(value));
1629 }
1630
Jeff Sharkeye66c1772013-09-20 14:30:59 -07001631 public static long readLongAttribute(XmlPullParser in, String name, long defaultValue) {
1632 final String value = in.getAttributeValue(null, name);
Fyodor Kupolov9f9bf952016-11-01 11:31:32 -07001633 if (TextUtils.isEmpty(value)) {
1634 return defaultValue;
1635 }
Jeff Sharkeye66c1772013-09-20 14:30:59 -07001636 try {
1637 return Long.parseLong(value);
1638 } catch (NumberFormatException e) {
1639 return defaultValue;
1640 }
1641 }
1642
Jeff Sharkeyded7b752013-03-22 13:43:41 -07001643 public static long readLongAttribute(XmlPullParser in, String name) throws IOException {
1644 final String value = in.getAttributeValue(null, name);
1645 try {
1646 return Long.parseLong(value);
1647 } catch (NumberFormatException e) {
1648 throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
1649 }
1650 }
1651
1652 public static void writeLongAttribute(XmlSerializer out, String name, long value)
1653 throws IOException {
1654 out.attribute(null, name, Long.toString(value));
1655 }
1656
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001657 public static float readFloatAttribute(XmlPullParser in, String name) throws IOException {
1658 final String value = in.getAttributeValue(null, name);
1659 try {
1660 return Float.parseFloat(value);
1661 } catch (NumberFormatException e) {
1662 throw new ProtocolException("problem parsing " + name + "=" + value + " as long");
1663 }
1664 }
1665
1666 public static void writeFloatAttribute(XmlSerializer out, String name, float value)
1667 throws IOException {
1668 out.attribute(null, name, Float.toString(value));
1669 }
1670
Jeff Sharkeyded7b752013-03-22 13:43:41 -07001671 public static boolean readBooleanAttribute(XmlPullParser in, String name) {
1672 final String value = in.getAttributeValue(null, name);
1673 return Boolean.parseBoolean(value);
1674 }
1675
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001676 public static boolean readBooleanAttribute(XmlPullParser in, String name,
1677 boolean defaultValue) {
1678 final String value = in.getAttributeValue(null, name);
Jeff Sharkey9bc99f82018-11-27 13:38:56 -07001679 if (TextUtils.isEmpty(value)) {
Jeff Sharkey77d218e2014-09-06 12:20:37 -07001680 return defaultValue;
1681 } else {
1682 return Boolean.parseBoolean(value);
1683 }
1684 }
1685
Jeff Sharkeyded7b752013-03-22 13:43:41 -07001686 public static void writeBooleanAttribute(XmlSerializer out, String name, boolean value)
1687 throws IOException {
1688 out.attribute(null, name, Boolean.toString(value));
1689 }
Craig Mautneref73ee12014-04-23 11:45:37 -07001690
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001691 public static Uri readUriAttribute(XmlPullParser in, String name) {
1692 final String value = in.getAttributeValue(null, name);
1693 return (value != null) ? Uri.parse(value) : null;
1694 }
1695
1696 public static void writeUriAttribute(XmlSerializer out, String name, Uri value)
1697 throws IOException {
1698 if (value != null) {
1699 out.attribute(null, name, value.toString());
1700 }
1701 }
1702
1703 public static String readStringAttribute(XmlPullParser in, String name) {
1704 return in.getAttributeValue(null, name);
1705 }
1706
Jeff Sharkey17bebd22017-07-19 21:00:38 -06001707 public static void writeStringAttribute(XmlSerializer out, String name, CharSequence value)
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001708 throws IOException {
1709 if (value != null) {
Jeff Sharkey17bebd22017-07-19 21:00:38 -06001710 out.attribute(null, name, value.toString());
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001711 }
1712 }
1713
1714 public static byte[] readByteArrayAttribute(XmlPullParser in, String name) {
1715 final String value = in.getAttributeValue(null, name);
Jeff Sharkey9bc99f82018-11-27 13:38:56 -07001716 if (!TextUtils.isEmpty(value)) {
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001717 return Base64.decode(value, Base64.DEFAULT);
1718 } else {
1719 return null;
1720 }
1721 }
1722
1723 public static void writeByteArrayAttribute(XmlSerializer out, String name, byte[] value)
1724 throws IOException {
1725 if (value != null) {
1726 out.attribute(null, name, Base64.encodeToString(value, Base64.DEFAULT));
1727 }
1728 }
1729
1730 public static Bitmap readBitmapAttribute(XmlPullParser in, String name) {
1731 final byte[] value = readByteArrayAttribute(in, name);
1732 if (value != null) {
1733 return BitmapFactory.decodeByteArray(value, 0, value.length);
1734 } else {
1735 return null;
1736 }
1737 }
1738
Jeff Sharkey02bd7842014-10-06 15:14:27 -07001739 @Deprecated
Jeff Sharkey1cb2d0d2014-07-30 16:45:01 -07001740 public static void writeBitmapAttribute(XmlSerializer out, String name, Bitmap value)
1741 throws IOException {
1742 if (value != null) {
1743 final ByteArrayOutputStream os = new ByteArrayOutputStream();
1744 value.compress(CompressFormat.PNG, 90, os);
1745 writeByteArrayAttribute(out, name, os.toByteArray());
1746 }
1747 }
1748
Craig Mautneref73ee12014-04-23 11:45:37 -07001749 /** @hide */
1750 public interface WriteMapCallback {
1751 /**
1752 * Called from writeMapXml when an Object type is not recognized. The implementer
1753 * must write out the entire element including start and end tags.
1754 *
1755 * @param v The object to be written out
1756 * @param name The mapping key for v. Must be written into the "name" attribute of the
1757 * start tag.
1758 * @param out The XML output stream.
1759 * @throws XmlPullParserException on unrecognized Object type.
1760 * @throws IOException on XmlSerializer serialization errors.
1761 * @hide
1762 */
1763 public void writeUnknownObject(Object v, String name, XmlSerializer out)
1764 throws XmlPullParserException, IOException;
1765 }
1766
1767 /** @hide */
1768 public interface ReadMapCallback {
1769 /**
1770 * Called from readThisMapXml when a START_TAG is not recognized. The input stream
1771 * is positioned within the start tag so that attributes can be read using in.getAttribute.
1772 *
1773 * @param in the XML input stream
1774 * @param tag the START_TAG that was not recognized.
1775 * @return the Object parsed from the stream which will be put into the map.
1776 * @throws XmlPullParserException if the START_TAG is not recognized.
1777 * @throws IOException on XmlPullParser serialization errors.
1778 * @hide
1779 */
1780 public Object readThisUnknownObjectXml(XmlPullParser in, String tag)
1781 throws XmlPullParserException, IOException;
1782 }
Tom Taylor087ff0b2009-12-18 10:08:35 -08001783}