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