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