Home | History | Annotate | Download | only in res
      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 dalvik.annotation.optimization.FastNative;
     24 
     25 import org.xmlpull.v1.XmlPullParserException;
     26 
     27 import java.io.IOException;
     28 import java.io.InputStream;
     29 import java.io.Reader;
     30 
     31 /**
     32  * Wrapper around a compiled XML file.
     33  *
     34  * {@hide}
     35  */
     36 final class XmlBlock {
     37     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) {
     65                 mAssets.xmlBlockGone(hashCode());
     66             }
     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 {
     80         Parser(long parseState, XmlBlock block) {
     81             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
    380             // we want to count on aapt doing the conversion for us.
    381             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
    390             // we want to count on aapt doing the conversion for us.
    391             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
    399             // we want to count on aapt doing the conversion for us.
    400             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
    409             // we want to count on aapt doing the conversion for us.
    410             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
    419             // we want to count on aapt doing the conversion for us.
    420             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 
    463         /*package*/ long mParseState;
    464         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      */
    481     XmlBlock(AssetManager assets, long xmlBlock) {
    482         mAssets = assets;
    483         mNative = xmlBlock;
    484         mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false);
    485     }
    486 
    487     private final AssetManager mAssets;
    488     private final long mNative;
    489     /*package*/ final StringBlock mStrings;
    490     private boolean mOpen = true;
    491     private int mOpenCount = 1;
    492 
    493     private static final native long nativeCreate(byte[] data,
    494                                                  int offset,
    495                                                  int size);
    496     private static final native long nativeGetStringBlock(long obj);
    497     private static final native long nativeCreateParseState(long obj);
    498     private static final native void nativeDestroyParseState(long state);
    499     private static final native void nativeDestroy(long obj);
    500 
    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);
    535 }
    536