1/*
2 * Copyright (C) 2009 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 dex.reader;
18
19import static dex.structure.DexEncodedValueType.*;
20import dex.reader.DexFileReader.FieldIdItem;
21import dex.structure.DexAnnotation;
22import dex.structure.DexEncodedValue;
23import dex.structure.DexEncodedValueType;
24
25import java.util.ArrayList;
26import java.util.List;
27
28/* package */final class DexEncodedValueImpl implements DexEncodedValue {
29
30    private final DexBuffer buffer;
31    private byte typeAndValueArg;
32    private DexEncodedValueType type;
33    private String[] stringPool;
34    private Object value;
35    private int[] typeIds;
36    private final FieldIdItem[] fieldIdItems;
37    private final DexAnnotation annotation;
38
39    /**
40     *
41     * @param buffer
42     *            the buffer with the correct position
43     * @param annotation
44     * @param stringPool
45     * @param fieldIdItems
46     */
47    public DexEncodedValueImpl(DexBuffer buffer, DexAnnotation annotation,
48            int[] typeIds, String[] stringPool, FieldIdItem[] fieldIdItems) {
49        this.buffer = buffer;
50        this.annotation = annotation;
51        this.typeIds = typeIds;
52        this.stringPool = stringPool;
53        this.fieldIdItems = fieldIdItems;
54        parseValue();
55    }
56
57    private void parseValue() {
58        typeAndValueArg = buffer.readUByte();
59        type = DexEncodedValueType.get(typeAndValueArg);
60        int valueArg = DexEncodedValueType.valueArg(typeAndValueArg);
61        switch (type) {
62        case VALUE_BYTE:
63            value = getByteValue(valueArg);
64            break;
65        case VALUE_SHORT:
66            value = getShortValue(valueArg);
67            break;
68        case VALUE_CHAR:
69            value = getCharValue(valueArg);
70            break;
71        case VALUE_INT:
72            value = getIntValue(valueArg);
73            break;
74        case VALUE_LONG:
75            value = getLongValue(valueArg);
76            break;
77        case VALUE_FLOAT:
78            value = getFloatValue(valueArg);
79            break;
80        case VALUE_DOUBLE:
81            value = getDoubleValue(valueArg);
82            break;
83        case VALUE_STRING:
84            value = getStringValue(valueArg);
85            break;
86        case VALUE_TYPE:
87            value = getTypeValue(valueArg);
88            break;
89        case VALUE_FIELD:
90            value = getFieldValue(valueArg);
91            break;
92        case VALUE_METHOD:
93            value = getMethodValue(valueArg);
94            break;
95        case VALUE_ENUM:
96            value = getEnumValue(valueArg);
97            break;
98        case VALUE_ARRAY:
99            value = getArrayValue(valueArg);
100            break;
101        case VALUE_ANNOTATION:
102            value = getAnnotationValue(valueArg);
103            break;
104        case VALUE_NULL:
105            value = getNullValue(valueArg);
106            break;
107        case VALUE_BOOLEAN:
108            value = getBooleanValue(valueArg);
109            break;
110        default:
111            throw new IllegalArgumentException("DexEncodedValueType " + type
112                    + " not recognized");
113        }
114    }
115
116    /**
117     * VALUE_BOOLEAN 0x1f boolean (0...1) (none) one-bit value; 0 for false and
118     * 1 for true. The bit is represented in the value_arg.
119     */
120    private Boolean getBooleanValue(int valueArg) {
121        return valueArg == 1;
122    }
123
124    /** VALUE_NULL 0x1e (none; must be 0) (none) null reference value */
125    private Object getNullValue(int valueArg) {
126        return null; // must be like that!
127    }
128
129    /**
130     * VALUE_ANNOTATION 0x1d (none; must be 0) encoded_annotation a
131     * sub-annotation, in the format specified by "encoded_annotation Format"
132     * below. The size of the value is implicit in the encoding.
133     */
134    private Object getAnnotationValue(int valueArg) {
135        // use the buffer directly to get adjusted offset
136        return new DexEncodedAnnotationImpl(buffer, annotation, typeIds,
137                stringPool, fieldIdItems);
138    }
139
140    /**
141     * VALUE_ARRAY 0x1c (none; must be 0) encoded_array an array of values, in
142     * the format specified by "encoded_array Format" below. The size of the
143     * value is implicit in the encoding.
144     */
145    private List<DexEncodedValue> getArrayValue(int valueArg) {
146        int size = buffer.readUleb128();
147        List<DexEncodedValue> values = new ArrayList<DexEncodedValue>(size);
148        for (int i = 0; i < size; i++) {
149            values.add(new DexEncodedValueImpl(buffer, annotation, typeIds,
150                    stringPool, fieldIdItems));
151        }
152        return values;
153    }
154
155    /**
156     * VALUE_ENUM 0x1b size - 1 (0...3) ubyte[size] unsigned (zero-extended)
157     * four-byte integer value, interpreted as an index into the field_ids
158     * section and representing the value of an enumerated type constant
159     */
160    private Object getEnumValue(int valueArg) {
161        int fieldOffset = buffer.readInt(valueArg + 1);
162        FieldIdItem fieldIdItem = fieldIdItems[fieldOffset];
163        // FORMAT La/b/E;!CONSTANT
164        String constantName = stringPool[fieldIdItem.name_idx];
165        String typeName = stringPool[typeIds[fieldIdItem.type_idx]];
166        return typeName + "!" + constantName;
167    }
168
169    /**
170     * VALUE_METHOD 0x1a size - 1 (0...3) ubyte[size] unsigned (zero-extended)
171     * four-byte integer value, interpreted as an index into the method_ids
172     * section and representing a reflective method value
173     */
174    private Object getMethodValue(int valueArg) {
175        // FIXME lookup value
176        buffer.skip(valueArg + 1);
177        return null;
178    }
179
180    /**
181     * VALUE_FIELD 0x19 size - 1 (0...3) ubyte[size] unsigned (zero-extended)
182     * four-byte integer value, interpreted as an index into the field_ids
183     * section and representing a reflective field value
184     */
185    private Object getFieldValue(int valueArg) {
186        int fieldOffset = buffer.readInt(valueArg + 1);
187        FieldIdItem fieldIdItem = fieldIdItems[fieldOffset];
188        // FORMAT La/b/E;!CONSTANT
189        String fieldName = stringPool[fieldIdItem.name_idx];
190        String typeName = stringPool[typeIds[fieldIdItem.type_idx]];
191        return typeName + "!" + fieldName;
192    }
193
194    /**
195     * VALUE_TYPE 0x18 size - 1 (0...3) ubyte[size] unsigned (zero-extended)
196     * four-byte integer value, interpreted as an index into the type_ids
197     * section and representing a reflective type/class value
198     */
199    private Object getTypeValue(int valueArg) {
200        valueArg++; // size - 1 (0...3)
201        // FIXME SPEC!! states: unsigned (zero-extended) four-byte integer value
202        return stringPool[typeIds[buffer.readInt(valueArg)]];
203    }
204
205    /**
206     * VALUE_STRING 0x17 size - 1 (0...3) ubyte[size] unsigned (zero-extended)
207     * four-byte integer value, interpreted as an index into the string_ids
208     * section and representing a string value
209     */
210    private Object getStringValue(int valueArg) {
211        valueArg++;
212        return stringPool[buffer.readInt(valueArg)];
213    }
214
215    /**
216     * VALUE_DOUBLE 0x11 size - 1 (0...7) ubyte[size] eight-byte bit pattern,
217     * zero-extended to the right, and interpreted as an IEEE754 64-bit floating
218     * point value
219     */
220    private Object getDoubleValue(int valueArg) {
221        return buffer.readDouble(valueArg + 1);
222    }
223
224    /**
225     * VALUE_FLOAT 0x10 size - 1 (0...3) ubyte[size] four-byte bit pattern,
226     * zero-extended to the right, and interpreted as an IEEE754 32-bit floating
227     * point value
228     */
229    private Float getFloatValue(int valueArg) {
230        return buffer.readFloat(valueArg + 1);
231    }
232
233    /**
234     * VALUE_LONG 0x06 size - 1 (0...7) ubyte[size] signed eight-byte integer
235     * value, sign-extended
236     */
237    private Long getLongValue(int valueArg) {
238        return buffer.readLong(valueArg + 1);
239    }
240
241    /**
242     * VALUE_INT 0x04 size - 1 (0...3) ubyte[size] signed four-byte integer
243     * value, sign-extended
244     */
245    private Integer getIntValue(int valueArg) {
246        return buffer.readInt(valueArg + 1);
247    }
248
249    /**
250     * VALUE_CHAR 0x03 size - 1 (0...1) ubyte[size] unsigned two-byte integer
251     * value, zero-extended
252     */
253    private Character getCharValue(int valueArg) {
254        return buffer.readChar(valueArg + 1);
255    }
256
257    /**
258     * VALUE_SHORT 0x02 size - 1 (0...1) ubyte[size] signed two-byte integer
259     * value, sign-extended
260     */
261    private Short getShortValue(int valueArg) {
262        return buffer.readShort(valueArg + 1);
263    }
264
265    /**
266     * VALUE_BYTE 0x00 (none; must be 0) ubyte[1] signed one-byte integer value
267     */
268    private Byte getByteValue(int valueArg) {
269        assert valueArg == 0 : "Illegal valueArg for VALUE_BYTE: " + valueArg;
270        return null;
271    }
272
273    public DexEncodedValueType getType() {
274        return type;
275    }
276
277    public Object getValue() {
278        return value;
279    }
280
281    @Override
282    public String toString() {
283        StringBuilder builder = new StringBuilder();
284        builder.append("=");
285        if (type == VALUE_ARRAY) {
286            if (getValue() instanceof List<?>) {
287                List<?> values = (List<?>) getValue();
288                for (Object object : values) {
289                    DexEncodedValue val = (DexEncodedValue) object;
290                    builder.append(val.getValue());
291                }
292            }
293        } else {
294            builder.append(getValue());
295        }
296        return builder.toString();
297    }
298}
299