Home | History | Annotate | Download | only in res
      1 /*
      2  * Copyright (C) 2008 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 com.android.ide.common.rendering.api.AttrResourceValue;
     20 import com.android.ide.common.rendering.api.LayoutLog;
     21 import com.android.ide.common.rendering.api.RenderResources;
     22 import com.android.ide.common.rendering.api.ResourceValue;
     23 import com.android.ide.common.rendering.api.StyleResourceValue;
     24 import com.android.internal.util.XmlUtils;
     25 import com.android.layoutlib.bridge.Bridge;
     26 import com.android.layoutlib.bridge.android.BridgeContext;
     27 import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
     28 import com.android.layoutlib.bridge.impl.ParserFactory;
     29 import com.android.layoutlib.bridge.impl.ResourceHelper;
     30 import com.android.resources.ResourceType;
     31 
     32 import org.xmlpull.v1.XmlPullParser;
     33 import org.xmlpull.v1.XmlPullParserException;
     34 
     35 import android.graphics.drawable.Drawable;
     36 import android.util.DisplayMetrics;
     37 import android.util.TypedValue;
     38 import android.view.LayoutInflater_Delegate;
     39 import android.view.ViewGroup.LayoutParams;
     40 
     41 import java.io.File;
     42 import java.util.Arrays;
     43 import java.util.Map;
     44 
     45 /**
     46  * Custom implementation of TypedArray to handle non compiled resources.
     47  */
     48 public final class BridgeTypedArray extends TypedArray {
     49 
     50     private final BridgeResources mBridgeResources;
     51     private final BridgeContext mContext;
     52     private final boolean mPlatformFile;
     53 
     54     private final ResourceValue[] mResourceData;
     55     private final String[] mNames;
     56     private final boolean[] mIsFramework;
     57 
     58     public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len,
     59             boolean platformFile) {
     60         super(resources, null, null, 0);
     61         mBridgeResources = resources;
     62         mContext = context;
     63         mPlatformFile = platformFile;
     64         mResourceData = new ResourceValue[len];
     65         mNames = new String[len];
     66         mIsFramework = new boolean[len];
     67     }
     68 
     69     /**
     70      * A bridge-specific method that sets a value in the type array
     71      * @param index the index of the value in the TypedArray
     72      * @param name the name of the attribute
     73      * @param isFramework whether the attribute is in the android namespace.
     74      * @param value the value of the attribute
     75      */
     76     public void bridgeSetValue(int index, String name, boolean isFramework, ResourceValue value) {
     77         mResourceData[index] = value;
     78         mNames[index] = name;
     79         mIsFramework[index] = isFramework;
     80     }
     81 
     82     /**
     83      * Seals the array after all calls to
     84      * {@link #bridgeSetValue(int, String, boolean, ResourceValue)} have been done.
     85      * <p/>This allows to compute the list of non default values, permitting
     86      * {@link #getIndexCount()} to return the proper value.
     87      */
     88     public void sealArray() {
     89         // fills TypedArray.mIndices which is used to implement getIndexCount/getIndexAt
     90         // first count the array size
     91         int count = 0;
     92         for (int i = 0; i < mResourceData.length; i++) {
     93             ResourceValue data = mResourceData[i];
     94             if (data != null) {
     95                 if (RenderResources.REFERENCE_NULL.equals(data.getValue())) {
     96                     // No need to store this resource value. This saves needless checking for
     97                     // "@null" every time  an attribute is requested.
     98                     mResourceData[i] = null;
     99                 } else {
    100                     count++;
    101                 }
    102             }
    103         }
    104 
    105         // allocate the table with an extra to store the size
    106         mIndices = new int[count+1];
    107         mIndices[0] = count;
    108 
    109         // fill the array with the indices.
    110         int index = 1;
    111         for (int i = 0 ; i < mResourceData.length ; i++) {
    112             if (mResourceData[i] != null) {
    113                 mIndices[index++] = i;
    114             }
    115         }
    116     }
    117 
    118     /**
    119      * Return the number of values in this array.
    120      */
    121     @Override
    122     public int length() {
    123         return mResourceData.length;
    124     }
    125 
    126     /**
    127      * Return the Resources object this array was loaded from.
    128      */
    129     @Override
    130     public Resources getResources() {
    131         return mBridgeResources;
    132     }
    133 
    134     /**
    135      * Retrieve the styled string value for the attribute at <var>index</var>.
    136      *
    137      * @param index Index of attribute to retrieve.
    138      *
    139      * @return CharSequence holding string data.  May be styled.  Returns
    140      *         null if the attribute is not defined.
    141      */
    142     @Override
    143     public CharSequence getText(int index) {
    144         // FIXME: handle styled strings!
    145         return getString(index);
    146     }
    147 
    148     /**
    149      * Retrieve the string value for the attribute at <var>index</var>.
    150      *
    151      * @param index Index of attribute to retrieve.
    152      *
    153      * @return String holding string data.  Any styling information is
    154      * removed.  Returns null if the attribute is not defined.
    155      */
    156     @Override
    157     public String getString(int index) {
    158         if (!hasValue(index)) {
    159             return null;
    160         }
    161         // As unfortunate as it is, it's possible to use enums with all attribute formats,
    162         // not just integers/enums. So, we need to search the enums always. In case
    163         // enums are used, the returned value is an integer.
    164         Integer v = resolveEnumAttribute(index);
    165         return v == null ? mResourceData[index].getValue() : String.valueOf((int) v);
    166     }
    167 
    168     /**
    169      * Retrieve the boolean value for the attribute at <var>index</var>.
    170      *
    171      * @param index Index of attribute to retrieve.
    172      * @param defValue Value to return if the attribute is not defined.
    173      *
    174      * @return Attribute boolean value, or defValue if not defined.
    175      */
    176     @Override
    177     public boolean getBoolean(int index, boolean defValue) {
    178         String s = getString(index);
    179         return s == null ? defValue : XmlUtils.convertValueToBoolean(s, defValue);
    180 
    181     }
    182 
    183     /**
    184      * Retrieve the integer value for the attribute at <var>index</var>.
    185      *
    186      * @param index Index of attribute to retrieve.
    187      * @param defValue Value to return if the attribute is not defined.
    188      *
    189      * @return Attribute int value, or defValue if not defined.
    190      */
    191     @Override
    192     public int getInt(int index, int defValue) {
    193         String s = getString(index);
    194         try {
    195             if (s != null) {
    196                 return XmlUtils.convertValueToInt(s, defValue);
    197             }
    198         } catch (NumberFormatException e) {
    199             Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
    200                     String.format("\"%1$s\" in attribute \"%2$s\" is not a valid integer",
    201                             s, mNames[index]),
    202                     null);
    203             return defValue;
    204         }
    205         return defValue;
    206     }
    207 
    208     /**
    209      * Retrieve the float value for the attribute at <var>index</var>.
    210      *
    211      * @param index Index of attribute to retrieve.
    212      *
    213      * @return Attribute float value, or defValue if not defined..
    214      */
    215     @Override
    216     public float getFloat(int index, float defValue) {
    217         String s = getString(index);
    218         try {
    219             if (s != null) {
    220                     return Float.parseFloat(s);
    221             }
    222         } catch (NumberFormatException e) {
    223             Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
    224                     String.format("\"%1$s\" in attribute \"%2$s\" cannot be converted to float.",
    225                             s, mNames[index]),
    226                     null);
    227         }
    228         return defValue;
    229     }
    230 
    231     /**
    232      * Retrieve the color value for the attribute at <var>index</var>.  If
    233      * the attribute references a color resource holding a complex
    234      * {@link android.content.res.ColorStateList}, then the default color from
    235      * the set is returned.
    236      *
    237      * @param index Index of attribute to retrieve.
    238      * @param defValue Value to return if the attribute is not defined or
    239      *                 not a resource.
    240      *
    241      * @return Attribute color value, or defValue if not defined.
    242      */
    243     @Override
    244     public int getColor(int index, int defValue) {
    245         if (index < 0 || index >= mResourceData.length) {
    246             return defValue;
    247         }
    248 
    249         if (mResourceData[index] == null) {
    250             return defValue;
    251         }
    252 
    253         ColorStateList colorStateList = ResourceHelper.getColorStateList(
    254                 mResourceData[index], mContext);
    255         if (colorStateList != null) {
    256             return colorStateList.getDefaultColor();
    257         }
    258 
    259         return defValue;
    260     }
    261 
    262     /**
    263      * Retrieve the ColorStateList for the attribute at <var>index</var>.
    264      * The value may be either a single solid color or a reference to
    265      * a color or complex {@link android.content.res.ColorStateList} description.
    266      *
    267      * @param index Index of attribute to retrieve.
    268      *
    269      * @return ColorStateList for the attribute, or null if not defined.
    270      */
    271     @Override
    272     public ColorStateList getColorStateList(int index) {
    273         if (!hasValue(index)) {
    274             return null;
    275         }
    276 
    277         ResourceValue resValue = mResourceData[index];
    278         String value = resValue.getValue();
    279 
    280         if (value == null) {
    281             return null;
    282         }
    283 
    284         // let the framework inflate the ColorStateList from the XML file.
    285         File f = new File(value);
    286         if (f.isFile()) {
    287             try {
    288                 XmlPullParser parser = ParserFactory.create(f);
    289 
    290                 BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
    291                         parser, mContext, resValue.isFramework());
    292                 try {
    293                     return ColorStateList.createFromXml(mContext.getResources(), blockParser);
    294                 } finally {
    295                     blockParser.ensurePopped();
    296                 }
    297             } catch (XmlPullParserException e) {
    298                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
    299                         "Failed to configure parser for " + value, e, null);
    300                 return null;
    301             } catch (Exception e) {
    302                 // this is an error and not warning since the file existence is checked before
    303                 // attempting to parse it.
    304                 Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
    305                         "Failed to parse file " + value, e, null);
    306 
    307                 return null;
    308             }
    309         }
    310 
    311         try {
    312             int color = ResourceHelper.getColor(value);
    313             return ColorStateList.valueOf(color);
    314         } catch (NumberFormatException e) {
    315             Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e, null);
    316         }
    317 
    318         return null;
    319     }
    320 
    321     /**
    322      * Retrieve the integer value for the attribute at <var>index</var>.
    323      *
    324      * @param index Index of attribute to retrieve.
    325      * @param defValue Value to return if the attribute is not defined or
    326      *                 not a resource.
    327      *
    328      * @return Attribute integer value, or defValue if not defined.
    329      */
    330     @Override
    331     public int getInteger(int index, int defValue) {
    332         return getInt(index, defValue);
    333     }
    334 
    335     /**
    336      * Retrieve a dimensional unit attribute at <var>index</var>.  Unit
    337      * conversions are based on the current {@link DisplayMetrics}
    338      * associated with the resources this {@link TypedArray} object
    339      * came from.
    340      *
    341      * @param index Index of attribute to retrieve.
    342      * @param defValue Value to return if the attribute is not defined or
    343      *                 not a resource.
    344      *
    345      * @return Attribute dimension value multiplied by the appropriate
    346      * metric, or defValue if not defined.
    347      *
    348      * @see #getDimensionPixelOffset
    349      * @see #getDimensionPixelSize
    350      */
    351     @Override
    352     public float getDimension(int index, float defValue) {
    353         String s = getString(index);
    354         if (s == null) {
    355             return defValue;
    356         }
    357         // Check if the value is a magic constant that doesn't require a unit.
    358         try {
    359             int i = Integer.parseInt(s);
    360             if (i == LayoutParams.MATCH_PARENT || i == LayoutParams.WRAP_CONTENT) {
    361                 return i;
    362             }
    363         } catch (NumberFormatException ignored) {
    364             // pass
    365         }
    366 
    367         if (ResourceHelper.parseFloatAttribute(mNames[index], s, mValue, true)) {
    368             return mValue.getDimension(mBridgeResources.getDisplayMetrics());
    369         }
    370 
    371         return defValue;
    372     }
    373 
    374     /**
    375      * Retrieve a dimensional unit attribute at <var>index</var> for use
    376      * as an offset in raw pixels.  This is the same as
    377      * {@link #getDimension}, except the returned value is converted to
    378      * integer pixels for you.  An offset conversion involves simply
    379      * truncating the base value to an integer.
    380      *
    381      * @param index Index of attribute to retrieve.
    382      * @param defValue Value to return if the attribute is not defined or
    383      *                 not a resource.
    384      *
    385      * @return Attribute dimension value multiplied by the appropriate
    386      * metric and truncated to integer pixels, or defValue if not defined.
    387      *
    388      * @see #getDimension
    389      * @see #getDimensionPixelSize
    390      */
    391     @Override
    392     public int getDimensionPixelOffset(int index, int defValue) {
    393         return (int) getDimension(index, defValue);
    394     }
    395 
    396     /**
    397      * Retrieve a dimensional unit attribute at <var>index</var> for use
    398      * as a size in raw pixels.  This is the same as
    399      * {@link #getDimension}, except the returned value is converted to
    400      * integer pixels for use as a size.  A size conversion involves
    401      * rounding the base value, and ensuring that a non-zero base value
    402      * is at least one pixel in size.
    403      *
    404      * @param index Index of attribute to retrieve.
    405      * @param defValue Value to return if the attribute is not defined or
    406      *                 not a resource.
    407      *
    408      * @return Attribute dimension value multiplied by the appropriate
    409      * metric and truncated to integer pixels, or defValue if not defined.
    410      *
    411      * @see #getDimension
    412      * @see #getDimensionPixelOffset
    413      */
    414     @Override
    415     public int getDimensionPixelSize(int index, int defValue) {
    416         try {
    417             return getDimension(index);
    418         } catch (RuntimeException e) {
    419             String s = getString(index);
    420 
    421             if (s != null) {
    422                 // looks like we were unable to resolve the dimension value
    423                 Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
    424                         String.format("\"%1$s\" in attribute \"%2$s\" is not a valid format.",
    425                                 s, mNames[index]), null);
    426             }
    427 
    428             return defValue;
    429         }
    430     }
    431 
    432     /**
    433      * Special version of {@link #getDimensionPixelSize} for retrieving
    434      * {@link android.view.ViewGroup}'s layout_width and layout_height
    435      * attributes.  This is only here for performance reasons; applications
    436      * should use {@link #getDimensionPixelSize}.
    437      *
    438      * @param index Index of the attribute to retrieve.
    439      * @param name Textual name of attribute for error reporting.
    440      *
    441      * @return Attribute dimension value multiplied by the appropriate
    442      * metric and truncated to integer pixels.
    443      */
    444     @Override
    445     public int getLayoutDimension(int index, String name) {
    446         try {
    447             // this will throw an exception
    448             return getDimension(index);
    449         } catch (RuntimeException e) {
    450 
    451             if (LayoutInflater_Delegate.sIsInInclude) {
    452                 throw new RuntimeException();
    453             }
    454 
    455             Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
    456                     "You must supply a " + name + " attribute.", null);
    457 
    458             return 0;
    459         }
    460     }
    461 
    462     @Override
    463     public int getLayoutDimension(int index, int defValue) {
    464         return getDimensionPixelSize(index, defValue);
    465     }
    466 
    467     private int getDimension(int index) {
    468         String s = getString(index);
    469         if (s == null) {
    470             throw new RuntimeException();
    471         }
    472         // Check if the value is a magic constant that doesn't require a unit.
    473         try {
    474             int i = Integer.parseInt(s);
    475             if (i == LayoutParams.MATCH_PARENT || i == LayoutParams.WRAP_CONTENT) {
    476                 return i;
    477             }
    478         } catch (NumberFormatException ignored) {
    479             // pass
    480         }
    481         if (ResourceHelper.parseFloatAttribute(mNames[index], s, mValue, true)) {
    482             float f = mValue.getDimension(mBridgeResources.getDisplayMetrics());
    483 
    484             final int res = (int)(f+0.5f);
    485             if (res != 0) return res;
    486             if (f == 0) return 0;
    487             if (f > 0) return 1;
    488         }
    489 
    490         throw new RuntimeException();
    491     }
    492 
    493     /**
    494      * Retrieve a fractional unit attribute at <var>index</var>.
    495      *
    496      * @param index Index of attribute to retrieve.
    497      * @param base The base value of this fraction.  In other words, a
    498      *             standard fraction is multiplied by this value.
    499      * @param pbase The parent base value of this fraction.  In other
    500      *             words, a parent fraction (nn%p) is multiplied by this
    501      *             value.
    502      * @param defValue Value to return if the attribute is not defined or
    503      *                 not a resource.
    504      *
    505      * @return Attribute fractional value multiplied by the appropriate
    506      * base value, or defValue if not defined.
    507      */
    508     @Override
    509     public float getFraction(int index, int base, int pbase, float defValue) {
    510         String value = getString(index);
    511         if (value == null) {
    512             return defValue;
    513         }
    514 
    515         if (ResourceHelper.parseFloatAttribute(mNames[index], value, mValue, false)) {
    516             return mValue.getFraction(base, pbase);
    517         }
    518 
    519         // looks like we were unable to resolve the fraction value
    520         Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
    521                 String.format(
    522                         "\"%1$s\" in attribute \"%2$s\" cannot be converted to a fraction.",
    523                         value, mNames[index]), null);
    524 
    525         return defValue;
    526     }
    527 
    528     /**
    529      * Retrieve the resource identifier for the attribute at
    530      * <var>index</var>.  Note that attribute resource as resolved when
    531      * the overall {@link TypedArray} object is retrieved.  As a
    532      * result, this function will return the resource identifier of the
    533      * final resource value that was found, <em>not</em> necessarily the
    534      * original resource that was specified by the attribute.
    535      *
    536      * @param index Index of attribute to retrieve.
    537      * @param defValue Value to return if the attribute is not defined or
    538      *                 not a resource.
    539      *
    540      * @return Attribute resource identifier, or defValue if not defined.
    541      */
    542     @Override
    543     public int getResourceId(int index, int defValue) {
    544         if (index < 0 || index >= mResourceData.length) {
    545             return defValue;
    546         }
    547 
    548         // get the Resource for this index
    549         ResourceValue resValue = mResourceData[index];
    550 
    551         // no data, return the default value.
    552         if (resValue == null) {
    553             return defValue;
    554         }
    555 
    556         // check if this is a style resource
    557         if (resValue instanceof StyleResourceValue) {
    558             // get the id that will represent this style.
    559             return mContext.getDynamicIdByStyle((StyleResourceValue)resValue);
    560         }
    561 
    562         // if the attribute was a reference to a resource, and not a declaration of an id (@+id),
    563         // then the xml attribute value was "resolved" which leads us to a ResourceValue with a
    564         // valid getType() and getName() returning a resource name.
    565         // (and getValue() returning null!). We need to handle this!
    566         if (resValue.getResourceType() != null) {
    567             // if this is a framework id
    568             if (mPlatformFile || resValue.isFramework()) {
    569                 // look for idName in the android R classes
    570                 return mContext.getFrameworkResourceValue(
    571                         resValue.getResourceType(), resValue.getName(), defValue);
    572             }
    573 
    574             // look for idName in the project R class.
    575             return mContext.getProjectResourceValue(
    576                     resValue.getResourceType(), resValue.getName(), defValue);
    577         }
    578 
    579         // else, try to get the value, and resolve it somehow.
    580         String value = resValue.getValue();
    581         if (value == null) {
    582             return defValue;
    583         }
    584 
    585         // if the value is just an integer, return it.
    586         try {
    587             int i = Integer.parseInt(value);
    588             if (Integer.toString(i).equals(value)) {
    589                 return i;
    590             }
    591         } catch (NumberFormatException e) {
    592             // pass
    593         }
    594 
    595         // Handle the @id/<name>, @+id/<name> and @android:id/<name>
    596         // We need to return the exact value that was compiled (from the various R classes),
    597         // as these values can be reused internally with calls to findViewById().
    598         // There's a trick with platform layouts that not use "android:" but their IDs are in
    599         // fact in the android.R and com.android.internal.R classes.
    600         // The field mPlatformFile will indicate that all IDs are to be looked up in the android R
    601         // classes exclusively.
    602 
    603         // if this is a reference to an id, find it.
    604         if (value.startsWith("@id/") || value.startsWith("@+") ||
    605                 value.startsWith("@android:id/")) {
    606 
    607             int pos = value.indexOf('/');
    608             String idName = value.substring(pos + 1);
    609 
    610             // if this is a framework id
    611             if (mPlatformFile || value.startsWith("@android") || value.startsWith("@+android")) {
    612                 // look for idName in the android R classes
    613                 return mContext.getFrameworkResourceValue(ResourceType.ID, idName, defValue);
    614             }
    615 
    616             // look for idName in the project R class.
    617             return mContext.getProjectResourceValue(ResourceType.ID, idName, defValue);
    618         }
    619 
    620         // not a direct id valid reference? resolve it
    621         Integer idValue;
    622 
    623         if (resValue.isFramework()) {
    624             idValue = Bridge.getResourceId(resValue.getResourceType(),
    625                     resValue.getName());
    626         } else {
    627             idValue = mContext.getProjectCallback().getResourceId(
    628                     resValue.getResourceType(), resValue.getName());
    629         }
    630 
    631         if (idValue != null) {
    632             return idValue;
    633         }
    634 
    635         Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE,
    636                 String.format(
    637                     "Unable to resolve id \"%1$s\" for attribute \"%2$s\"", value, mNames[index]),
    638                     resValue);
    639 
    640         return defValue;
    641     }
    642 
    643     @Override
    644     public int getThemeAttributeId(int index, int defValue) {
    645         // TODO: Get the right Theme Attribute ID to enable caching of the drawables.
    646         return defValue;
    647     }
    648 
    649     /**
    650      * Retrieve the Drawable for the attribute at <var>index</var>.  This
    651      * gets the resource ID of the selected attribute, and uses
    652      * {@link Resources#getDrawable Resources.getDrawable} of the owning
    653      * Resources object to retrieve its Drawable.
    654      *
    655      * @param index Index of attribute to retrieve.
    656      *
    657      * @return Drawable for the attribute, or null if not defined.
    658      */
    659     @Override
    660     public Drawable getDrawable(int index) {
    661         if (!hasValue(index)) {
    662             return null;
    663         }
    664 
    665         ResourceValue value = mResourceData[index];
    666         return ResourceHelper.getDrawable(value, mContext);
    667     }
    668 
    669 
    670     /**
    671      * Retrieve the CharSequence[] for the attribute at <var>index</var>.
    672      * This gets the resource ID of the selected attribute, and uses
    673      * {@link Resources#getTextArray Resources.getTextArray} of the owning
    674      * Resources object to retrieve its String[].
    675      *
    676      * @param index Index of attribute to retrieve.
    677      *
    678      * @return CharSequence[] for the attribute, or null if not defined.
    679      */
    680     @Override
    681     public CharSequence[] getTextArray(int index) {
    682         String value = getString(index);
    683         if (value != null) {
    684             return new CharSequence[] { value };
    685         }
    686 
    687         return null;
    688     }
    689 
    690     @Override
    691     public int[] extractThemeAttrs() {
    692         // The drawables are always inflated with a Theme and we don't care about caching. So,
    693         // just return.
    694         return null;
    695     }
    696 
    697     @Override
    698     public int getChangingConfigurations() {
    699         // We don't care about caching. Any change in configuration is a fresh render. So,
    700         // just return.
    701         return 0;
    702     }
    703 
    704     /**
    705      * Retrieve the raw TypedValue for the attribute at <var>index</var>.
    706      *
    707      * @param index Index of attribute to retrieve.
    708      * @param outValue TypedValue object in which to place the attribute's
    709      *                 data.
    710      *
    711      * @return Returns true if the value was retrieved, else false.
    712      */
    713     @Override
    714     public boolean getValue(int index, TypedValue outValue) {
    715         String s = getString(index);
    716         return s != null && ResourceHelper.parseFloatAttribute(mNames[index], s, outValue, false);
    717     }
    718 
    719     /**
    720      * Determines whether there is an attribute at <var>index</var>.
    721      *
    722      * @param index Index of attribute to retrieve.
    723      *
    724      * @return True if the attribute has a value, false otherwise.
    725      */
    726     @Override
    727     public boolean hasValue(int index) {
    728         return index >= 0 && index < mResourceData.length && mResourceData[index] != null;
    729     }
    730 
    731     /**
    732      * Retrieve the raw TypedValue for the attribute at <var>index</var>
    733      * and return a temporary object holding its data.  This object is only
    734      * valid until the next call on to {@link TypedArray}.
    735      *
    736      * @param index Index of attribute to retrieve.
    737      *
    738      * @return Returns a TypedValue object if the attribute is defined,
    739      *         containing its data; otherwise returns null.  (You will not
    740      *         receive a TypedValue whose type is TYPE_NULL.)
    741      */
    742     @Override
    743     public TypedValue peekValue(int index) {
    744         if (index < 0 || index >= mResourceData.length) {
    745             return null;
    746         }
    747 
    748         if (getValue(index, mValue)) {
    749             return mValue;
    750         }
    751 
    752         return null;
    753     }
    754 
    755     /**
    756      * Returns a message about the parser state suitable for printing error messages.
    757      */
    758     @Override
    759     public String getPositionDescription() {
    760         return "<internal -- stub if needed>";
    761     }
    762 
    763     /**
    764      * Give back a previously retrieved TypedArray, for later re-use.
    765      */
    766     @Override
    767     public void recycle() {
    768         // pass
    769     }
    770 
    771     @Override
    772     public String toString() {
    773         return Arrays.toString(mResourceData);
    774     }
    775 
    776     /**
    777      * Searches for the string in the attributes (flag or enums) and returns the integer.
    778      * If found, it will return an integer matching the value.
    779      *
    780      * @param index Index of attribute to retrieve.
    781      *
    782      * @return Attribute int value, or null if not defined.
    783      */
    784     private Integer resolveEnumAttribute(int index) {
    785         // Get the map of attribute-constant -> IntegerValue
    786         Map<String, Integer> map = null;
    787         if (mIsFramework[index]) {
    788             map = Bridge.getEnumValues(mNames[index]);
    789         } else {
    790             // get the styleable matching the resolved name
    791             RenderResources res = mContext.getRenderResources();
    792             ResourceValue attr = res.getProjectResource(ResourceType.ATTR, mNames[index]);
    793             if (attr instanceof AttrResourceValue) {
    794                 map = ((AttrResourceValue) attr).getAttributeValues();
    795             }
    796         }
    797 
    798         if (map != null) {
    799             // accumulator to store the value of the 1+ constants.
    800             int result = 0;
    801             boolean found = false;
    802 
    803             // split the value in case this is a mix of several flags.
    804             String[] keywords = mResourceData[index].getValue().split("\\|");
    805             for (String keyword : keywords) {
    806                 Integer i = map.get(keyword.trim());
    807                 if (i != null) {
    808                     result |= i;
    809                     found = true;
    810                 }
    811                 // TODO: We should act smartly and log a warning for incorrect keywords. However,
    812                 // this method is currently called even if the resourceValue is not an enum.
    813             }
    814             if (found) {
    815                 return result;
    816             }
    817         }
    818 
    819         return null;
    820     }
    821 
    822     static TypedArray obtain(Resources res, int len) {
    823         return res instanceof BridgeResources ?
    824                 new BridgeTypedArray(((BridgeResources) res), null, len, true) : null;
    825     }
    826 }
    827