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