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