TypedArray.java revision 1d0b177754c81a20e272b91c6f703a634fe5c856
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 android.content.pm.ActivityInfo;
20import android.graphics.drawable.Drawable;
21import android.util.AttributeSet;
22import android.util.DisplayMetrics;
23import android.util.Log;
24import android.util.TypedValue;
25
26import com.android.internal.util.XmlUtils;
27
28import java.util.Arrays;
29
30/**
31 * Container for an array of values that were retrieved with
32 * {@link Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)}
33 * or {@link Resources#obtainAttributes}.  Be
34 * sure to call {@link #recycle} when done with them.
35 *
36 * The indices used to retrieve values from this structure correspond to
37 * the positions of the attributes given to obtainStyledAttributes.
38 */
39public class TypedArray {
40    private final Resources mResources;
41    /*package*/ XmlBlock.Parser mXml;
42    /*package*/ int[] mRsrcs;
43    /*package*/ int[] mData;
44    /*package*/ int[] mIndices;
45    /*package*/ int mLength;
46    /*package*/ TypedValue mValue = new TypedValue();
47
48    /**
49     * Return the number of values in this array.
50     */
51    public int length() {
52        return mLength;
53    }
54
55    /**
56     * Return the number of indices in the array that actually have data.
57     */
58    public int getIndexCount() {
59        return mIndices[0];
60    }
61
62    /**
63     * Return an index in the array that has data.
64     *
65     * @param at The index you would like to returned, ranging from 0 to
66     * {@link #getIndexCount()}.
67     *
68     * @return The index at the given offset, which can be used with
69     * {@link #getValue} and related APIs.
70     */
71    public int getIndex(int at) {
72        return mIndices[1+at];
73    }
74
75    /**
76     * Return the Resources object this array was loaded from.
77     */
78    public Resources getResources() {
79        return mResources;
80    }
81
82    /**
83     * Retrieve the styled string value for the attribute at <var>index</var>.
84     *
85     * @param index Index of attribute to retrieve.
86     *
87     * @return CharSequence holding string data.  May be styled.  Returns
88     *         null if the attribute is not defined.
89     */
90    public CharSequence getText(int index) {
91        index *= AssetManager.STYLE_NUM_ENTRIES;
92        final int[] data = mData;
93        final int type = data[index+AssetManager.STYLE_TYPE];
94        if (type == TypedValue.TYPE_NULL) {
95            return null;
96        } else if (type == TypedValue.TYPE_STRING) {
97            return loadStringValueAt(index);
98        }
99
100        TypedValue v = mValue;
101        if (getValueAt(index, v)) {
102            Log.w(Resources.TAG, "Converting to string: " + v);
103            return v.coerceToString();
104        }
105        Log.w(Resources.TAG, "getString of bad type: 0x"
106              + Integer.toHexString(type));
107        return null;
108    }
109
110    /**
111     * Retrieve the string value for the attribute at <var>index</var>.
112     *
113     * @param index Index of attribute to retrieve.
114     *
115     * @return String holding string data.  Any styling information is
116     * removed.  Returns null if the attribute is not defined.
117     */
118    public String getString(int index) {
119        index *= AssetManager.STYLE_NUM_ENTRIES;
120        final int[] data = mData;
121        final int type = data[index+AssetManager.STYLE_TYPE];
122        if (type == TypedValue.TYPE_NULL) {
123            return null;
124        } else if (type == TypedValue.TYPE_STRING) {
125            return loadStringValueAt(index).toString();
126        }
127
128        TypedValue v = mValue;
129        if (getValueAt(index, v)) {
130            Log.w(Resources.TAG, "Converting to string: " + v);
131            CharSequence cs = v.coerceToString();
132            return cs != null ? cs.toString() : null;
133        }
134        Log.w(Resources.TAG, "getString of bad type: 0x"
135              + Integer.toHexString(type));
136        return null;
137    }
138
139    /**
140     * Retrieve the string value for the attribute at <var>index</var>, but
141     * only if that string comes from an immediate value in an XML file.  That
142     * is, this does not allow references to string resources, string
143     * attributes, or conversions from other types.  As such, this method
144     * will only return strings for TypedArray objects that come from
145     * attributes in an XML file.
146     *
147     * @param index Index of attribute to retrieve.
148     *
149     * @return String holding string data.  Any styling information is
150     * removed.  Returns null if the attribute is not defined or is not
151     * an immediate string value.
152     */
153    public String getNonResourceString(int index) {
154        index *= AssetManager.STYLE_NUM_ENTRIES;
155        final int[] data = mData;
156        final int type = data[index+AssetManager.STYLE_TYPE];
157        if (type == TypedValue.TYPE_STRING) {
158            final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
159            if (cookie < 0) {
160                return mXml.getPooledString(
161                    data[index+AssetManager.STYLE_DATA]).toString();
162            }
163        }
164        return null;
165    }
166
167    /**
168     * @hide
169     * Retrieve the string value for the attribute at <var>index</var> that is
170     * not allowed to change with the given configurations.
171     *
172     * @param index Index of attribute to retrieve.
173     * @param allowedChangingConfigs Bit mask of configurations from
174     * {@link Configuration}.NATIVE_CONFIG_* that are allowed to change.
175     *
176     * @return String holding string data.  Any styling information is
177     * removed.  Returns null if the attribute is not defined.
178     */
179    public String getNonConfigurationString(int index, int allowedChangingConfigs) {
180        index *= AssetManager.STYLE_NUM_ENTRIES;
181        final int[] data = mData;
182        final int type = data[index+AssetManager.STYLE_TYPE];
183        if ((data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS]&~allowedChangingConfigs) != 0) {
184            return null;
185        }
186        if (type == TypedValue.TYPE_NULL) {
187            return null;
188        } else if (type == TypedValue.TYPE_STRING) {
189            return loadStringValueAt(index).toString();
190        }
191
192        TypedValue v = mValue;
193        if (getValueAt(index, v)) {
194            Log.w(Resources.TAG, "Converting to string: " + v);
195            CharSequence cs = v.coerceToString();
196            return cs != null ? cs.toString() : null;
197        }
198        Log.w(Resources.TAG, "getString of bad type: 0x"
199              + Integer.toHexString(type));
200        return null;
201    }
202
203    /**
204     * Retrieve the boolean value for the attribute at <var>index</var>.
205     *
206     * @param index Index of attribute to retrieve.
207     * @param defValue Value to return if the attribute is not defined.
208     *
209     * @return Attribute boolean value, or defValue if not defined.
210     */
211    public boolean getBoolean(int index, boolean defValue) {
212        index *= AssetManager.STYLE_NUM_ENTRIES;
213        final int[] data = mData;
214        final int type = data[index+AssetManager.STYLE_TYPE];
215        if (type == TypedValue.TYPE_NULL) {
216            return defValue;
217        } else if (type >= TypedValue.TYPE_FIRST_INT
218            && type <= TypedValue.TYPE_LAST_INT) {
219            return data[index+AssetManager.STYLE_DATA] != 0;
220        }
221
222        TypedValue v = mValue;
223        if (getValueAt(index, v)) {
224            Log.w(Resources.TAG, "Converting to boolean: " + v);
225            return XmlUtils.convertValueToBoolean(
226                v.coerceToString(), defValue);
227        }
228        Log.w(Resources.TAG, "getBoolean of bad type: 0x"
229              + Integer.toHexString(type));
230        return defValue;
231    }
232
233    /**
234     * Retrieve the integer value for the attribute at <var>index</var>.
235     *
236     * @param index Index of attribute to retrieve.
237     * @param defValue Value to return if the attribute is not defined.
238     *
239     * @return Attribute int value, or defValue if not defined.
240     */
241    public int getInt(int index, int defValue) {
242        index *= AssetManager.STYLE_NUM_ENTRIES;
243        final int[] data = mData;
244        final int type = data[index+AssetManager.STYLE_TYPE];
245        if (type == TypedValue.TYPE_NULL) {
246            return defValue;
247        } else if (type >= TypedValue.TYPE_FIRST_INT
248            && type <= TypedValue.TYPE_LAST_INT) {
249            return data[index+AssetManager.STYLE_DATA];
250        }
251
252        TypedValue v = mValue;
253        if (getValueAt(index, v)) {
254            Log.w(Resources.TAG, "Converting to int: " + v);
255            return XmlUtils.convertValueToInt(
256                v.coerceToString(), defValue);
257        }
258        Log.w(Resources.TAG, "getInt of bad type: 0x"
259              + Integer.toHexString(type));
260        return defValue;
261    }
262
263    /**
264     * Retrieve the float value for the attribute at <var>index</var>.
265     *
266     * @param index Index of attribute to retrieve.
267     *
268     * @return Attribute float value, or defValue if not defined..
269     */
270    public float getFloat(int index, float defValue) {
271        index *= AssetManager.STYLE_NUM_ENTRIES;
272        final int[] data = mData;
273        final int type = data[index+AssetManager.STYLE_TYPE];
274        if (type == TypedValue.TYPE_NULL) {
275            return defValue;
276        } else if (type == TypedValue.TYPE_FLOAT) {
277            return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]);
278        } else if (type >= TypedValue.TYPE_FIRST_INT
279            && type <= TypedValue.TYPE_LAST_INT) {
280            return data[index+AssetManager.STYLE_DATA];
281        }
282
283        TypedValue v = mValue;
284        if (getValueAt(index, v)) {
285            Log.w(Resources.TAG, "Converting to float: " + v);
286            CharSequence str = v.coerceToString();
287            if (str != null) {
288                return Float.parseFloat(str.toString());
289            }
290        }
291        Log.w(Resources.TAG, "getFloat of bad type: 0x"
292              + Integer.toHexString(type));
293        return defValue;
294    }
295
296    /**
297     * Retrieve the color value for the attribute at <var>index</var>.  If
298     * the attribute references a color resource holding a complex
299     * {@link android.content.res.ColorStateList}, then the default color from
300     * the set is returned.
301     *
302     * @param index Index of attribute to retrieve.
303     * @param defValue Value to return if the attribute is not defined or
304     *                 not a resource.
305     *
306     * @return Attribute color value, or defValue if not defined.
307     */
308    public int getColor(int index, int defValue) {
309        index *= AssetManager.STYLE_NUM_ENTRIES;
310        final int[] data = mData;
311        final int type = data[index+AssetManager.STYLE_TYPE];
312        if (type == TypedValue.TYPE_NULL) {
313            return defValue;
314        } else if (type >= TypedValue.TYPE_FIRST_INT
315            && type <= TypedValue.TYPE_LAST_INT) {
316            return data[index+AssetManager.STYLE_DATA];
317        } else if (type == TypedValue.TYPE_STRING) {
318            final TypedValue value = mValue;
319            if (getValueAt(index, value)) {
320                ColorStateList csl = mResources.loadColorStateList(
321                        value, value.resourceId);
322                return csl.getDefaultColor();
323            }
324            return defValue;
325        }
326
327        throw new UnsupportedOperationException("Can't convert to color: type=0x"
328                + Integer.toHexString(type));
329    }
330
331    /**
332     * Retrieve the ColorStateList for the attribute at <var>index</var>.
333     * The value may be either a single solid color or a reference to
334     * a color or complex {@link android.content.res.ColorStateList} description.
335     *
336     * @param index Index of attribute to retrieve.
337     *
338     * @return ColorStateList for the attribute, or null if not defined.
339     */
340    public ColorStateList getColorStateList(int index) {
341        final TypedValue value = mValue;
342        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
343            return mResources.loadColorStateList(value, value.resourceId);
344        }
345        return null;
346    }
347
348    /**
349     * Retrieve the integer value for the attribute at <var>index</var>.
350     *
351     * @param index Index of attribute to retrieve.
352     * @param defValue Value to return if the attribute is not defined or
353     *                 not a resource.
354     *
355     * @return Attribute integer value, or defValue if not defined.
356     */
357    public int getInteger(int index, int defValue) {
358        index *= AssetManager.STYLE_NUM_ENTRIES;
359        final int[] data = mData;
360        final int type = data[index+AssetManager.STYLE_TYPE];
361        if (type == TypedValue.TYPE_NULL) {
362            return defValue;
363        } else if (type >= TypedValue.TYPE_FIRST_INT
364            && type <= TypedValue.TYPE_LAST_INT) {
365            return data[index+AssetManager.STYLE_DATA];
366        }
367
368        throw new UnsupportedOperationException("Can't convert to integer: type=0x"
369                + Integer.toHexString(type));
370    }
371
372    /**
373     * Retrieve a dimensional unit attribute at <var>index</var>.  Unit
374     * conversions are based on the current {@link DisplayMetrics}
375     * associated with the resources this {@link TypedArray} object
376     * came from.
377     *
378     * @param index Index of attribute to retrieve.
379     * @param defValue Value to return if the attribute is not defined or
380     *                 not a resource.
381     *
382     * @return Attribute dimension value multiplied by the appropriate
383     * metric, or defValue if not defined.
384     *
385     * @see #getDimensionPixelOffset
386     * @see #getDimensionPixelSize
387     */
388    public float getDimension(int index, float defValue) {
389        index *= AssetManager.STYLE_NUM_ENTRIES;
390        final int[] data = mData;
391        final int type = data[index+AssetManager.STYLE_TYPE];
392        if (type == TypedValue.TYPE_NULL) {
393            return defValue;
394        } else if (type == TypedValue.TYPE_DIMENSION) {
395            return TypedValue.complexToDimension(
396                data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
397        }
398
399        throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
400                + Integer.toHexString(type));
401    }
402
403    /**
404     * Retrieve a dimensional unit attribute at <var>index</var> for use
405     * as an offset in raw pixels.  This is the same as
406     * {@link #getDimension}, except the returned value is converted to
407     * integer pixels for you.  An offset conversion involves simply
408     * truncating the base value to an integer.
409     *
410     * @param index Index of attribute to retrieve.
411     * @param defValue Value to return if the attribute is not defined or
412     *                 not a resource.
413     *
414     * @return Attribute dimension value multiplied by the appropriate
415     * metric and truncated to integer pixels, or defValue if not defined.
416     *
417     * @see #getDimension
418     * @see #getDimensionPixelSize
419     */
420    public int getDimensionPixelOffset(int index, int defValue) {
421        index *= AssetManager.STYLE_NUM_ENTRIES;
422        final int[] data = mData;
423        final int type = data[index+AssetManager.STYLE_TYPE];
424        if (type == TypedValue.TYPE_NULL) {
425            return defValue;
426        } else if (type == TypedValue.TYPE_DIMENSION) {
427            return TypedValue.complexToDimensionPixelOffset(
428                data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
429        }
430
431        throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
432                + Integer.toHexString(type));
433    }
434
435    /**
436     * Retrieve a dimensional unit attribute at <var>index</var> for use
437     * as a size in raw pixels.  This is the same as
438     * {@link #getDimension}, except the returned value is converted to
439     * integer pixels for use as a size.  A size conversion involves
440     * rounding the base value, and ensuring that a non-zero base value
441     * is at least one pixel in size.
442     *
443     * @param index Index of attribute to retrieve.
444     * @param defValue Value to return if the attribute is not defined or
445     *                 not a resource.
446     *
447     * @return Attribute dimension value multiplied by the appropriate
448     * metric and truncated to integer pixels, or defValue if not defined.
449     *
450     * @see #getDimension
451     * @see #getDimensionPixelOffset
452     */
453    public int getDimensionPixelSize(int index, int defValue) {
454        index *= AssetManager.STYLE_NUM_ENTRIES;
455        final int[] data = mData;
456        final int type = data[index+AssetManager.STYLE_TYPE];
457        if (type == TypedValue.TYPE_NULL) {
458            return defValue;
459        } else if (type == TypedValue.TYPE_DIMENSION) {
460            return TypedValue.complexToDimensionPixelSize(
461                data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
462        }
463
464        throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
465                + Integer.toHexString(type));
466    }
467
468    /**
469     * Special version of {@link #getDimensionPixelSize} for retrieving
470     * {@link android.view.ViewGroup}'s layout_width and layout_height
471     * attributes.  This is only here for performance reasons; applications
472     * should use {@link #getDimensionPixelSize}.
473     *
474     * @param index Index of the attribute to retrieve.
475     * @param name Textual name of attribute for error reporting.
476     *
477     * @return Attribute dimension value multiplied by the appropriate
478     * metric and truncated to integer pixels.
479     */
480    public int getLayoutDimension(int index, String name) {
481        index *= AssetManager.STYLE_NUM_ENTRIES;
482        final int[] data = mData;
483        final int type = data[index+AssetManager.STYLE_TYPE];
484        if (type >= TypedValue.TYPE_FIRST_INT
485                && type <= TypedValue.TYPE_LAST_INT) {
486            return data[index+AssetManager.STYLE_DATA];
487        } else if (type == TypedValue.TYPE_DIMENSION) {
488            return TypedValue.complexToDimensionPixelSize(
489                data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
490        }
491
492        throw new RuntimeException(getPositionDescription()
493                + ": You must supply a " + name + " attribute.");
494    }
495
496    /**
497     * Special version of {@link #getDimensionPixelSize} for retrieving
498     * {@link android.view.ViewGroup}'s layout_width and layout_height
499     * attributes.  This is only here for performance reasons; applications
500     * should use {@link #getDimensionPixelSize}.
501     *
502     * @param index Index of the attribute to retrieve.
503     * @param defValue The default value to return if this attribute is not
504     * default or contains the wrong type of data.
505     *
506     * @return Attribute dimension value multiplied by the appropriate
507     * metric and truncated to integer pixels.
508     */
509    public int getLayoutDimension(int index, int defValue) {
510        index *= AssetManager.STYLE_NUM_ENTRIES;
511        final int[] data = mData;
512        final int type = data[index+AssetManager.STYLE_TYPE];
513        if (type >= TypedValue.TYPE_FIRST_INT
514                && type <= TypedValue.TYPE_LAST_INT) {
515            return data[index+AssetManager.STYLE_DATA];
516        } else if (type == TypedValue.TYPE_DIMENSION) {
517            return TypedValue.complexToDimensionPixelSize(
518                data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
519        }
520
521        return defValue;
522    }
523
524    /**
525     * Retrieve a fractional unit attribute at <var>index</var>.
526     *
527     * @param index Index of attribute to retrieve.
528     * @param base The base value of this fraction.  In other words, a
529     *             standard fraction is multiplied by this value.
530     * @param pbase The parent base value of this fraction.  In other
531     *             words, a parent fraction (nn%p) is multiplied by this
532     *             value.
533     * @param defValue Value to return if the attribute is not defined or
534     *                 not a resource.
535     *
536     * @return Attribute fractional value multiplied by the appropriate
537     * base value, or defValue if not defined.
538     */
539    public float getFraction(int index, int base, int pbase, float defValue) {
540        index *= AssetManager.STYLE_NUM_ENTRIES;
541        final int[] data = mData;
542        final int type = data[index+AssetManager.STYLE_TYPE];
543        if (type == TypedValue.TYPE_NULL) {
544            return defValue;
545        } else if (type == TypedValue.TYPE_FRACTION) {
546            return TypedValue.complexToFraction(
547                data[index+AssetManager.STYLE_DATA], base, pbase);
548        }
549
550        throw new UnsupportedOperationException("Can't convert to fraction: type=0x"
551                + Integer.toHexString(type));
552    }
553
554    /**
555     * Retrieve the resource identifier for the attribute at
556     * <var>index</var>.  Note that attribute resource as resolved when
557     * the overall {@link TypedArray} object is retrieved.  As a
558     * result, this function will return the resource identifier of the
559     * final resource value that was found, <em>not</em> necessarily the
560     * original resource that was specified by the attribute.
561     *
562     * @param index Index of attribute to retrieve.
563     * @param defValue Value to return if the attribute is not defined or
564     *                 not a resource.
565     *
566     * @return Attribute resource identifier, or defValue if not defined.
567     */
568    public int getResourceId(int index, int defValue) {
569        index *= AssetManager.STYLE_NUM_ENTRIES;
570        final int[] data = mData;
571        if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) {
572            final int resid = data[index+AssetManager.STYLE_RESOURCE_ID];
573            if (resid != 0) {
574                return resid;
575            }
576        }
577        return defValue;
578    }
579
580    /**
581     * Retrieve the Drawable for the attribute at <var>index</var>.  This
582     * gets the resource ID of the selected attribute, and uses
583     * {@link Resources#getDrawable Resources.getDrawable} of the owning
584     * Resources object to retrieve its Drawable.
585     *
586     * @param index Index of attribute to retrieve.
587     *
588     * @return Drawable for the attribute, or null if not defined.
589     */
590    public Drawable getDrawable(int index) {
591        final TypedValue value = mValue;
592        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
593            if (false) {
594                System.out.println("******************************************************************");
595                System.out.println("Got drawable resource: type="
596                                   + value.type
597                                   + " str=" + value.string
598                                   + " int=0x" + Integer.toHexString(value.data)
599                                   + " cookie=" + value.assetCookie);
600                System.out.println("******************************************************************");
601            }
602            return mResources.loadDrawable(value, value.resourceId);
603        }
604        return null;
605    }
606
607    /**
608     * Retrieve the CharSequence[] for the attribute at <var>index</var>.
609     * This gets the resource ID of the selected attribute, and uses
610     * {@link Resources#getTextArray Resources.getTextArray} of the owning
611     * Resources object to retrieve its String[].
612     *
613     * @param index Index of attribute to retrieve.
614     *
615     * @return CharSequence[] for the attribute, or null if not defined.
616     */
617    public CharSequence[] getTextArray(int index) {
618        final TypedValue value = mValue;
619        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
620            if (false) {
621                System.out.println("******************************************************************");
622                System.out.println("Got drawable resource: type="
623                                   + value.type
624                                   + " str=" + value.string
625                                   + " int=0x" + Integer.toHexString(value.data)
626                                   + " cookie=" + value.assetCookie);
627                System.out.println("******************************************************************");
628            }
629            return mResources.getTextArray(value.resourceId);
630        }
631        return null;
632    }
633
634    /**
635     * Retrieve the raw TypedValue for the attribute at <var>index</var>.
636     *
637     * @param index Index of attribute to retrieve.
638     * @param outValue TypedValue object in which to place the attribute's
639     *                 data.
640     *
641     * @return Returns true if the value was retrieved, else false.
642     */
643    public boolean getValue(int index, TypedValue outValue) {
644        return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue);
645    }
646
647    /**
648     * Determines whether there is an attribute at <var>index</var>.
649     *
650     * @param index Index of attribute to retrieve.
651     *
652     * @return True if the attribute has a value, false otherwise.
653     */
654    public boolean hasValue(int index) {
655        index *= AssetManager.STYLE_NUM_ENTRIES;
656        final int[] data = mData;
657        final int type = data[index+AssetManager.STYLE_TYPE];
658        return type != TypedValue.TYPE_NULL;
659    }
660
661    /**
662     * Retrieve the raw TypedValue for the attribute at <var>index</var>
663     * and return a temporary object holding its data.  This object is only
664     * valid until the next call on to {@link TypedArray}.
665     *
666     * @param index Index of attribute to retrieve.
667     *
668     * @return Returns a TypedValue object if the attribute is defined,
669     *         containing its data; otherwise returns null.  (You will not
670     *         receive a TypedValue whose type is TYPE_NULL.)
671     */
672    public TypedValue peekValue(int index) {
673        final TypedValue value = mValue;
674        if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
675            return value;
676        }
677        return null;
678    }
679
680    /**
681     * Returns a message about the parser state suitable for printing error messages.
682     */
683    public String getPositionDescription() {
684        return mXml != null ? mXml.getPositionDescription() : "<internal>";
685    }
686
687    /**
688     * Give back a previously retrieved array, for later re-use.
689     */
690    public void recycle() {
691        synchronized (mResources.mAccessLock) {
692            TypedArray cached = mResources.mCachedStyledAttributes;
693            if (cached == null || cached.mData.length < mData.length) {
694                mXml = null;
695                mResources.mCachedStyledAttributes = this;
696            }
697        }
698    }
699
700    private boolean getValueAt(int index, TypedValue outValue) {
701        final int[] data = mData;
702        final int type = data[index+AssetManager.STYLE_TYPE];
703        if (type == TypedValue.TYPE_NULL) {
704            return false;
705        }
706        outValue.type = type;
707        outValue.data = data[index+AssetManager.STYLE_DATA];
708        outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
709        outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
710        outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];
711        outValue.density = data[index+AssetManager.STYLE_DENSITY];
712        outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
713        return true;
714    }
715
716    private CharSequence loadStringValueAt(int index) {
717        final int[] data = mData;
718        final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
719        if (cookie < 0) {
720            if (mXml != null) {
721                return mXml.getPooledString(
722                    data[index+AssetManager.STYLE_DATA]);
723            }
724            return null;
725        }
726        //System.out.println("Getting pooled from: " + v);
727        return mResources.mAssets.getPooledString(
728            cookie, data[index+AssetManager.STYLE_DATA]);
729    }
730
731    /*package*/ TypedArray(Resources resources, int[] data, int[] indices, int len) {
732        mResources = resources;
733        mData = data;
734        mIndices = indices;
735        mLength = len;
736    }
737
738    public String toString() {
739        return Arrays.toString(mData);
740    }
741}
742