• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
BridgeTypedArray(BridgeResources resources, BridgeContext context, int len, boolean platformFile)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      */
bridgeSetValue(int index, String name, boolean isFramework, ResourceValue value)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      */
sealArray()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
length()116     public int length() {
117         return mResourceData.length;
118     }
119 
120     /**
121      * Return the Resources object this array was loaded from.
122      */
123     @Override
getResources()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
getText(int index)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
getString(int index)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
getBoolean(int index, boolean defValue)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
getInt(int index, int defValue)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
getFloat(int index, float defValue)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
getColor(int index, int defValue)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
getColorStateList(int index)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
getInteger(int index, int defValue)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
getDimension(int index, float defValue)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
getDimensionPixelOffset(int index, int defValue)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
getDimensionPixelSize(int index, int defValue)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
getLayoutDimension(int index, String name)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
getLayoutDimension(int index, int defValue)559     public int getLayoutDimension(int index, int defValue) {
560         return getDimensionPixelSize(index, defValue);
561     }
562 
getDimension(int index)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
getFraction(int index, int base, int pbase, float defValue)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
getResourceId(int index, int defValue)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
getDrawable(int index)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
getTextArray(int index)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
getValue(int index, TypedValue outValue)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
hasValue(int index)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
peekValue(int index)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
getPositionDescription()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
recycle()900     public void recycle() {
901         // pass
902     }
903 
904     @Override
toString()905     public String toString() {
906         return Arrays.toString(mResourceData);
907     }
908  }
909