blob: e6b957414ea82309e12fb945679855cb50e8712d [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -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
17package android.content.res;
18
19import android.util.TypedValue;
Dianne Hackborn2269d1572010-02-24 19:54:22 -080020
21import com.android.internal.util.XmlUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022
John Reck32995222016-10-07 11:02:20 -070023import dalvik.annotation.optimization.FastNative;
24
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import org.xmlpull.v1.XmlPullParserException;
26
27import java.io.IOException;
28import java.io.InputStream;
29import java.io.Reader;
30
31/**
32 * Wrapper around a compiled XML file.
33 *
34 * {@hide}
35 */
Adam Lesinski7fb38312018-01-23 03:17:26 -080036final class XmlBlock {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037 private static final boolean DEBUG=false;
38
39 public XmlBlock(byte[] data) {
40 mAssets = null;
41 mNative = nativeCreate(data, 0, data.length);
42 mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
43 }
44
45 public XmlBlock(byte[] data, int offset, int size) {
46 mAssets = null;
47 mNative = nativeCreate(data, offset, size);
48 mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
49 }
50
51 public void close() {
52 synchronized (this) {
53 if (mOpen) {
54 mOpen = false;
55 decOpenCountLocked();
56 }
57 }
58 }
59
60 private void decOpenCountLocked() {
61 mOpenCount--;
62 if (mOpenCount == 0) {
63 nativeDestroy(mNative);
64 if (mAssets != null) {
Dianne Hackbornc3b91fd2010-02-23 17:25:30 -080065 mAssets.xmlBlockGone(hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066 }
67 }
68 }
69
70 public XmlResourceParser newParser() {
71 synchronized (this) {
72 if (mNative != 0) {
73 return new Parser(nativeCreateParseState(mNative), this);
74 }
75 return null;
76 }
77 }
78
79 /*package*/ final class Parser implements XmlResourceParser {
Ashok Bhat896043d2014-01-17 16:02:38 +000080 Parser(long parseState, XmlBlock block) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 mParseState = parseState;
82 mBlock = block;
83 block.mOpenCount++;
84 }
85
86 public void setFeature(String name, boolean state) throws XmlPullParserException {
87 if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
88 return;
89 }
90 if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) {
91 return;
92 }
93 throw new XmlPullParserException("Unsupported feature: " + name);
94 }
95 public boolean getFeature(String name) {
96 if (FEATURE_PROCESS_NAMESPACES.equals(name)) {
97 return true;
98 }
99 if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {
100 return true;
101 }
102 return false;
103 }
104 public void setProperty(String name, Object value) throws XmlPullParserException {
105 throw new XmlPullParserException("setProperty() not supported");
106 }
107 public Object getProperty(String name) {
108 return null;
109 }
110 public void setInput(Reader in) throws XmlPullParserException {
111 throw new XmlPullParserException("setInput() not supported");
112 }
113 public void setInput(InputStream inputStream, String inputEncoding) throws XmlPullParserException {
114 throw new XmlPullParserException("setInput() not supported");
115 }
116 public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException {
117 throw new XmlPullParserException("defineEntityReplacementText() not supported");
118 }
119 public String getNamespacePrefix(int pos) throws XmlPullParserException {
120 throw new XmlPullParserException("getNamespacePrefix() not supported");
121 }
122 public String getInputEncoding() {
123 return null;
124 }
125 public String getNamespace(String prefix) {
126 throw new RuntimeException("getNamespace() not supported");
127 }
128 public int getNamespaceCount(int depth) throws XmlPullParserException {
129 throw new XmlPullParserException("getNamespaceCount() not supported");
130 }
131 public String getPositionDescription() {
132 return "Binary XML file line #" + getLineNumber();
133 }
134 public String getNamespaceUri(int pos) throws XmlPullParserException {
135 throw new XmlPullParserException("getNamespaceUri() not supported");
136 }
137 public int getColumnNumber() {
138 return -1;
139 }
140 public int getDepth() {
141 return mDepth;
142 }
143 public String getText() {
144 int id = nativeGetText(mParseState);
145 return id >= 0 ? mStrings.get(id).toString() : null;
146 }
147 public int getLineNumber() {
148 return nativeGetLineNumber(mParseState);
149 }
150 public int getEventType() throws XmlPullParserException {
151 return mEventType;
152 }
153 public boolean isWhitespace() throws XmlPullParserException {
154 // whitespace was stripped by aapt.
155 return false;
156 }
157 public String getPrefix() {
158 throw new RuntimeException("getPrefix not supported");
159 }
160 public char[] getTextCharacters(int[] holderForStartAndLength) {
161 String txt = getText();
162 char[] chars = null;
163 if (txt != null) {
164 holderForStartAndLength[0] = 0;
165 holderForStartAndLength[1] = txt.length();
166 chars = new char[txt.length()];
167 txt.getChars(0, txt.length(), chars, 0);
168 }
169 return chars;
170 }
171 public String getNamespace() {
172 int id = nativeGetNamespace(mParseState);
173 return id >= 0 ? mStrings.get(id).toString() : "";
174 }
175 public String getName() {
176 int id = nativeGetName(mParseState);
177 return id >= 0 ? mStrings.get(id).toString() : null;
178 }
179 public String getAttributeNamespace(int index) {
180 int id = nativeGetAttributeNamespace(mParseState, index);
181 if (DEBUG) System.out.println("getAttributeNamespace of " + index + " = " + id);
182 if (id >= 0) return mStrings.get(id).toString();
183 else if (id == -1) return "";
184 throw new IndexOutOfBoundsException(String.valueOf(index));
185 }
186 public String getAttributeName(int index) {
187 int id = nativeGetAttributeName(mParseState, index);
188 if (DEBUG) System.out.println("getAttributeName of " + index + " = " + id);
189 if (id >= 0) return mStrings.get(id).toString();
190 throw new IndexOutOfBoundsException(String.valueOf(index));
191 }
192 public String getAttributePrefix(int index) {
193 throw new RuntimeException("getAttributePrefix not supported");
194 }
195 public boolean isEmptyElementTag() throws XmlPullParserException {
196 // XXX Need to detect this.
197 return false;
198 }
199 public int getAttributeCount() {
200 return mEventType == START_TAG ? nativeGetAttributeCount(mParseState) : -1;
201 }
202 public String getAttributeValue(int index) {
203 int id = nativeGetAttributeStringValue(mParseState, index);
204 if (DEBUG) System.out.println("getAttributeValue of " + index + " = " + id);
205 if (id >= 0) return mStrings.get(id).toString();
206
207 // May be some other type... check and try to convert if so.
208 int t = nativeGetAttributeDataType(mParseState, index);
209 if (t == TypedValue.TYPE_NULL) {
210 throw new IndexOutOfBoundsException(String.valueOf(index));
211 }
212
213 int v = nativeGetAttributeData(mParseState, index);
214 return TypedValue.coerceToString(t, v);
215 }
216 public String getAttributeType(int index) {
217 return "CDATA";
218 }
219 public boolean isAttributeDefault(int index) {
220 return false;
221 }
222 public int nextToken() throws XmlPullParserException,IOException {
223 return next();
224 }
225 public String getAttributeValue(String namespace, String name) {
226 int idx = nativeGetAttributeIndex(mParseState, namespace, name);
227 if (idx >= 0) {
228 if (DEBUG) System.out.println("getAttributeName of "
229 + namespace + ":" + name + " index = " + idx);
230 if (DEBUG) System.out.println(
231 "Namespace=" + getAttributeNamespace(idx)
232 + "Name=" + getAttributeName(idx)
233 + ", Value=" + getAttributeValue(idx));
234 return getAttributeValue(idx);
235 }
236 return null;
237 }
238 public int next() throws XmlPullParserException,IOException {
239 if (!mStarted) {
240 mStarted = true;
241 return START_DOCUMENT;
242 }
243 if (mParseState == 0) {
244 return END_DOCUMENT;
245 }
246 int ev = nativeNext(mParseState);
247 if (mDecNextDepth) {
248 mDepth--;
249 mDecNextDepth = false;
250 }
251 switch (ev) {
252 case START_TAG:
253 mDepth++;
254 break;
255 case END_TAG:
256 mDecNextDepth = true;
257 break;
258 }
259 mEventType = ev;
260 if (ev == END_DOCUMENT) {
261 // Automatically close the parse when we reach the end of
262 // a document, since the standard XmlPullParser interface
263 // doesn't have such an API so most clients will leave us
264 // dangling.
265 close();
266 }
267 return ev;
268 }
269 public void require(int type, String namespace, String name) throws XmlPullParserException,IOException {
270 if (type != getEventType()
271 || (namespace != null && !namespace.equals( getNamespace () ) )
272 || (name != null && !name.equals( getName() ) ) )
273 throw new XmlPullParserException( "expected "+ TYPES[ type ]+getPositionDescription());
274 }
275 public String nextText() throws XmlPullParserException,IOException {
276 if(getEventType() != START_TAG) {
277 throw new XmlPullParserException(
278 getPositionDescription()
279 + ": parser must be on START_TAG to read next text", this, null);
280 }
281 int eventType = next();
282 if(eventType == TEXT) {
283 String result = getText();
284 eventType = next();
285 if(eventType != END_TAG) {
286 throw new XmlPullParserException(
287 getPositionDescription()
288 + ": event TEXT it must be immediately followed by END_TAG", this, null);
289 }
290 return result;
291 } else if(eventType == END_TAG) {
292 return "";
293 } else {
294 throw new XmlPullParserException(
295 getPositionDescription()
296 + ": parser must be on START_TAG or TEXT to read text", this, null);
297 }
298 }
299 public int nextTag() throws XmlPullParserException,IOException {
300 int eventType = next();
301 if(eventType == TEXT && isWhitespace()) { // skip whitespace
302 eventType = next();
303 }
304 if (eventType != START_TAG && eventType != END_TAG) {
305 throw new XmlPullParserException(
306 getPositionDescription()
307 + ": expected start or end tag", this, null);
308 }
309 return eventType;
310 }
311
312 public int getAttributeNameResource(int index) {
313 return nativeGetAttributeResource(mParseState, index);
314 }
315
316 public int getAttributeListValue(String namespace, String attribute,
317 String[] options, int defaultValue) {
318 int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
319 if (idx >= 0) {
320 return getAttributeListValue(idx, options, defaultValue);
321 }
322 return defaultValue;
323 }
324 public boolean getAttributeBooleanValue(String namespace, String attribute,
325 boolean defaultValue) {
326 int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
327 if (idx >= 0) {
328 return getAttributeBooleanValue(idx, defaultValue);
329 }
330 return defaultValue;
331 }
332 public int getAttributeResourceValue(String namespace, String attribute,
333 int defaultValue) {
334 int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
335 if (idx >= 0) {
336 return getAttributeResourceValue(idx, defaultValue);
337 }
338 return defaultValue;
339 }
340 public int getAttributeIntValue(String namespace, String attribute,
341 int defaultValue) {
342 int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
343 if (idx >= 0) {
344 return getAttributeIntValue(idx, defaultValue);
345 }
346 return defaultValue;
347 }
348 public int getAttributeUnsignedIntValue(String namespace, String attribute,
349 int defaultValue)
350 {
351 int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
352 if (idx >= 0) {
353 return getAttributeUnsignedIntValue(idx, defaultValue);
354 }
355 return defaultValue;
356 }
357 public float getAttributeFloatValue(String namespace, String attribute,
358 float defaultValue) {
359 int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
360 if (idx >= 0) {
361 return getAttributeFloatValue(idx, defaultValue);
362 }
363 return defaultValue;
364 }
365
366 public int getAttributeListValue(int idx,
367 String[] options, int defaultValue) {
368 int t = nativeGetAttributeDataType(mParseState, idx);
369 int v = nativeGetAttributeData(mParseState, idx);
370 if (t == TypedValue.TYPE_STRING) {
371 return XmlUtils.convertValueToList(
372 mStrings.get(v), options, defaultValue);
373 }
374 return v;
375 }
376 public boolean getAttributeBooleanValue(int idx,
377 boolean defaultValue) {
378 int t = nativeGetAttributeDataType(mParseState, idx);
379 // Note: don't attempt to convert any other types, because
Adam Lesinskide898ff2014-01-29 18:20:45 -0800380 // we want to count on aapt doing the conversion for us.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 if (t >= TypedValue.TYPE_FIRST_INT &&
382 t <= TypedValue.TYPE_LAST_INT) {
383 return nativeGetAttributeData(mParseState, idx) != 0;
384 }
385 return defaultValue;
386 }
387 public int getAttributeResourceValue(int idx, int defaultValue) {
388 int t = nativeGetAttributeDataType(mParseState, idx);
389 // Note: don't attempt to convert any other types, because
Adam Lesinskide898ff2014-01-29 18:20:45 -0800390 // we want to count on aapt doing the conversion for us.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 if (t == TypedValue.TYPE_REFERENCE) {
392 return nativeGetAttributeData(mParseState, idx);
393 }
394 return defaultValue;
395 }
396 public int getAttributeIntValue(int idx, int defaultValue) {
397 int t = nativeGetAttributeDataType(mParseState, idx);
398 // Note: don't attempt to convert any other types, because
Adam Lesinskide898ff2014-01-29 18:20:45 -0800399 // we want to count on aapt doing the conversion for us.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 if (t >= TypedValue.TYPE_FIRST_INT &&
401 t <= TypedValue.TYPE_LAST_INT) {
402 return nativeGetAttributeData(mParseState, idx);
403 }
404 return defaultValue;
405 }
406 public int getAttributeUnsignedIntValue(int idx, int defaultValue) {
407 int t = nativeGetAttributeDataType(mParseState, idx);
408 // Note: don't attempt to convert any other types, because
Adam Lesinskide898ff2014-01-29 18:20:45 -0800409 // we want to count on aapt doing the conversion for us.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 if (t >= TypedValue.TYPE_FIRST_INT &&
411 t <= TypedValue.TYPE_LAST_INT) {
412 return nativeGetAttributeData(mParseState, idx);
413 }
414 return defaultValue;
415 }
416 public float getAttributeFloatValue(int idx, float defaultValue) {
417 int t = nativeGetAttributeDataType(mParseState, idx);
418 // Note: don't attempt to convert any other types, because
Adam Lesinskide898ff2014-01-29 18:20:45 -0800419 // we want to count on aapt doing the conversion for us.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800420 if (t == TypedValue.TYPE_FLOAT) {
421 return Float.intBitsToFloat(
422 nativeGetAttributeData(mParseState, idx));
423 }
424 throw new RuntimeException("not a float!");
425 }
426
427 public String getIdAttribute() {
428 int id = nativeGetIdAttribute(mParseState);
429 return id >= 0 ? mStrings.get(id).toString() : null;
430 }
431 public String getClassAttribute() {
432 int id = nativeGetClassAttribute(mParseState);
433 return id >= 0 ? mStrings.get(id).toString() : null;
434 }
435
436 public int getIdAttributeResourceValue(int defaultValue) {
437 //todo: create and use native method
438 return getAttributeResourceValue(null, "id", defaultValue);
439 }
440
441 public int getStyleAttribute() {
442 return nativeGetStyleAttribute(mParseState);
443 }
444
445 public void close() {
446 synchronized (mBlock) {
447 if (mParseState != 0) {
448 nativeDestroyParseState(mParseState);
449 mParseState = 0;
450 mBlock.decOpenCountLocked();
451 }
452 }
453 }
454
455 protected void finalize() throws Throwable {
456 close();
457 }
458
459 /*package*/ final CharSequence getPooledString(int id) {
460 return mStrings.get(id);
461 }
462
Ashok Bhat896043d2014-01-17 16:02:38 +0000463 /*package*/ long mParseState;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 private final XmlBlock mBlock;
465 private boolean mStarted = false;
466 private boolean mDecNextDepth = false;
467 private int mDepth = 0;
468 private int mEventType = START_DOCUMENT;
469 }
470
471 protected void finalize() throws Throwable {
472 close();
473 }
474
475 /**
476 * Create from an existing xml block native object. This is
477 * -extremely- dangerous -- only use it if you absolutely know what you
478 * are doing! The given native object must exist for the entire lifetime
479 * of this newly creating XmlBlock.
480 */
Adam Lesinski7fb38312018-01-23 03:17:26 -0800481 XmlBlock(AssetManager assets, long xmlBlock) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800482 mAssets = assets;
483 mNative = xmlBlock;
484 mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false);
485 }
486
Adam Lesinski7fb38312018-01-23 03:17:26 -0800487 private final AssetManager mAssets;
Ashok Bhat896043d2014-01-17 16:02:38 +0000488 private final long mNative;
Christopher Tate60201f22011-11-11 15:47:21 -0800489 /*package*/ final StringBlock mStrings;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 private boolean mOpen = true;
491 private int mOpenCount = 1;
492
Ashok Bhat896043d2014-01-17 16:02:38 +0000493 private static final native long nativeCreate(byte[] data,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 int offset,
495 int size);
Ashok Bhat896043d2014-01-17 16:02:38 +0000496 private static final native long nativeGetStringBlock(long obj);
Ashok Bhat896043d2014-01-17 16:02:38 +0000497 private static final native long nativeCreateParseState(long obj);
Ashok Bhat896043d2014-01-17 16:02:38 +0000498 private static final native void nativeDestroyParseState(long state);
Ashok Bhat896043d2014-01-17 16:02:38 +0000499 private static final native void nativeDestroy(long obj);
John Reck32995222016-10-07 11:02:20 -0700500
501 // ----------- @FastNative ------------------
502
503 @FastNative
504 /*package*/ static final native int nativeNext(long state);
505 @FastNative
506 private static final native int nativeGetNamespace(long state);
507 @FastNative
508 /*package*/ static final native int nativeGetName(long state);
509 @FastNative
510 private static final native int nativeGetText(long state);
511 @FastNative
512 private static final native int nativeGetLineNumber(long state);
513 @FastNative
514 private static final native int nativeGetAttributeCount(long state);
515 @FastNative
516 private static final native int nativeGetAttributeNamespace(long state, int idx);
517 @FastNative
518 private static final native int nativeGetAttributeName(long state, int idx);
519 @FastNative
520 private static final native int nativeGetAttributeResource(long state, int idx);
521 @FastNative
522 private static final native int nativeGetAttributeDataType(long state, int idx);
523 @FastNative
524 private static final native int nativeGetAttributeData(long state, int idx);
525 @FastNative
526 private static final native int nativeGetAttributeStringValue(long state, int idx);
527 @FastNative
528 private static final native int nativeGetIdAttribute(long state);
529 @FastNative
530 private static final native int nativeGetClassAttribute(long state);
531 @FastNative
532 private static final native int nativeGetStyleAttribute(long state);
533 @FastNative
534 private static final native int nativeGetAttributeIndex(long state, String namespace, String name);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535}