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