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