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 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 
     37     public XmlBlock(byte[] data) {
     38         mAssets = null;
     39         mNative = nativeCreate(data, 0, data.length);
     40         mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
     41     }
     42 
     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 
     49     public void close() {
     50         synchronized (this) {
     51             if (mOpen) {
     52                 mOpen = false;
     53                 decOpenCountLocked();
     54             }
     55         }
     56     }
     57 
     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 
     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 {
     78         Parser(int parseState, XmlBlock block) {
     79             mParseState = parseState;
     80             mBlock = block;
     81             block.mOpenCount++;
     82         }
     83 
     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         }
     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         }
    102         public void setProperty(String name, Object value) throws XmlPullParserException {
    103             throw new XmlPullParserException("setProperty() not supported");
    104         }
    105         public Object getProperty(String name) {
    106             return null;
    107         }
    108         public void setInput(Reader in) throws XmlPullParserException {
    109             throw new XmlPullParserException("setInput() not supported");
    110         }
    111         public void setInput(InputStream inputStream, String inputEncoding) throws XmlPullParserException {
    112             throw new XmlPullParserException("setInput() not supported");
    113         }
    114         public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException {
    115             throw new XmlPullParserException("defineEntityReplacementText() not supported");
    116         }
    117         public String getNamespacePrefix(int pos) throws XmlPullParserException {
    118             throw new XmlPullParserException("getNamespacePrefix() not supported");
    119         }
    120         public String getInputEncoding() {
    121             return null;
    122         }
    123         public String getNamespace(String prefix) {
    124             throw new RuntimeException("getNamespace() not supported");
    125         }
    126         public int getNamespaceCount(int depth) throws XmlPullParserException {
    127             throw new XmlPullParserException("getNamespaceCount() not supported");
    128         }
    129         public String getPositionDescription() {
    130             return "Binary XML file line #" + getLineNumber();
    131         }
    132         public String getNamespaceUri(int pos) throws XmlPullParserException {
    133             throw new XmlPullParserException("getNamespaceUri() not supported");
    134         }
    135         public int getColumnNumber() {
    136             return -1;
    137         }
    138         public int getDepth() {
    139             return mDepth;
    140         }
    141         public String getText() {
    142             int id = nativeGetText(mParseState);
    143             return id >= 0 ? mStrings.get(id).toString() : null;
    144         }
    145         public int getLineNumber() {
    146             return nativeGetLineNumber(mParseState);
    147         }
    148         public int getEventType() throws XmlPullParserException {
    149             return mEventType;
    150         }
    151         public boolean isWhitespace() throws XmlPullParserException {
    152             // whitespace was stripped by aapt.
    153             return false;
    154         }
    155         public String getPrefix() {
    156             throw new RuntimeException("getPrefix not supported");
    157         }
    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         }
    169         public String getNamespace() {
    170             int id = nativeGetNamespace(mParseState);
    171             return id >= 0 ? mStrings.get(id).toString() : "";
    172         }
    173         public String getName() {
    174             int id = nativeGetName(mParseState);
    175             return id >= 0 ? mStrings.get(id).toString() : null;
    176         }
    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         }
    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         }
    190         public String getAttributePrefix(int index) {
    191             throw new RuntimeException("getAttributePrefix not supported");
    192         }
    193         public boolean isEmptyElementTag() throws XmlPullParserException {
    194             // XXX Need to detect this.
    195             return false;
    196         }
    197         public int getAttributeCount() {
    198             return mEventType == START_TAG ? nativeGetAttributeCount(mParseState) : -1;
    199         }
    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         }
    214         public String getAttributeType(int index) {
    215             return "CDATA";
    216         }
    217         public boolean isAttributeDefault(int index) {
    218             return false;
    219         }
    220         public int nextToken() throws XmlPullParserException,IOException {
    221             return next();
    222         }
    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         }
    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         }
    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         }
    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         }
    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 
    310         public int getAttributeNameResource(int index) {
    311             return nativeGetAttributeResource(mParseState, index);
    312         }
    313 
    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         }
    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         }
    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         }
    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         }
    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         }
    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 
    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         }
    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 appt 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         }
    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 appt doing the conversion for us.
    389             if (t == TypedValue.TYPE_REFERENCE) {
    390                 return nativeGetAttributeData(mParseState, idx);
    391             }
    392             return defaultValue;
    393         }
    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 appt 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         }
    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 appt 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         }
    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 appt 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 
    425         public String getIdAttribute() {
    426             int id = nativeGetIdAttribute(mParseState);
    427             return id >= 0 ? mStrings.get(id).toString() : null;
    428         }
    429         public String getClassAttribute() {
    430             int id = nativeGetClassAttribute(mParseState);
    431             return id >= 0 ? mStrings.get(id).toString() : null;
    432         }
    433 
    434         public int getIdAttributeResourceValue(int defaultValue) {
    435             //todo: create and use native method
    436             return getAttributeResourceValue(null, "id", defaultValue);
    437         }
    438 
    439         public int getStyleAttribute() {
    440             return nativeGetStyleAttribute(mParseState);
    441         }
    442 
    443         public void close() {
    444             synchronized (mBlock) {
    445                 if (mParseState != 0) {
    446                     nativeDestroyParseState(mParseState);
    447                     mParseState = 0;
    448                     mBlock.decOpenCountLocked();
    449                 }
    450             }
    451         }
    452 
    453         protected void finalize() throws Throwable {
    454             close();
    455         }
    456 
    457         /*package*/ final CharSequence getPooledString(int id) {
    458             return mStrings.get(id);
    459         }
    460 
    461         /*package*/ int 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 
    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      */
    479     XmlBlock(AssetManager assets, int xmlBlock) {
    480         mAssets = assets;
    481         mNative = xmlBlock;
    482         mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false);
    483     }
    484 
    485     private final AssetManager mAssets;
    486     private final int mNative;
    487     /*package*/ final StringBlock mStrings;
    488     private boolean mOpen = true;
    489     private int mOpenCount = 1;
    490 
    491     private static final native int nativeCreate(byte[] data,
    492                                                  int offset,
    493                                                  int size);
    494     private static final native int nativeGetStringBlock(int obj);
    495 
    496     private static final native int nativeCreateParseState(int obj);
    497     /*package*/ static final native int nativeNext(int state);
    498     private static final native int nativeGetNamespace(int state);
    499     /*package*/ static final native int nativeGetName(int state);
    500     private static final native int nativeGetText(int state);
    501     private static final native int nativeGetLineNumber(int state);
    502     private static final native int nativeGetAttributeCount(int state);
    503     private static final native int nativeGetAttributeNamespace(int state, int idx);
    504     private static final native int nativeGetAttributeName(int state, int idx);
    505     private static final native int nativeGetAttributeResource(int state, int idx);
    506     private static final native int nativeGetAttributeDataType(int state, int idx);
    507     private static final native int nativeGetAttributeData(int state, int idx);
    508     private static final native int nativeGetAttributeStringValue(int state, int idx);
    509     private static final native int nativeGetIdAttribute(int state);
    510     private static final native int nativeGetClassAttribute(int state);
    511     private static final native int nativeGetStyleAttribute(int state);
    512     private static final native int nativeGetAttributeIndex(int state, String namespace, String name);
    513     private static final native void nativeDestroyParseState(int state);
    514 
    515     private static final native void nativeDestroy(int obj);
    516 }
    517