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