1/*
2 * Copyright (C) 2007 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 com.android.dexgen.dex.file;
18
19import com.android.dexgen.rop.cst.Constant;
20import com.android.dexgen.rop.cst.CstType;
21import com.android.dexgen.rop.type.Type;
22import com.android.dexgen.util.AnnotatedOutput;
23import com.android.dexgen.util.Hex;
24
25import java.util.Collection;
26import java.util.TreeMap;
27
28/**
29 * Type identifiers list section of a {@code .dex} file.
30 */
31public final class TypeIdsSection extends UniformItemSection {
32    /**
33     * {@code non-null;} map from types to {@link TypeIdItem} instances
34     */
35    private final TreeMap<Type, TypeIdItem> typeIds;
36
37    /**
38     * Constructs an instance. The file offset is initially unknown.
39     *
40     * @param file {@code non-null;} file that this instance is part of
41     */
42    public TypeIdsSection(DexFile file) {
43        super("type_ids", file, 4);
44
45        typeIds = new TreeMap<Type, TypeIdItem>();
46    }
47
48    /** {@inheritDoc} */
49    @Override
50    public Collection<? extends Item> items() {
51        return typeIds.values();
52    }
53
54    /** {@inheritDoc} */
55    @Override
56    public IndexedItem get(Constant cst) {
57        if (cst == null) {
58            throw new NullPointerException("cst == null");
59        }
60
61        throwIfNotPrepared();
62
63        Type type = ((CstType) cst).getClassType();
64        IndexedItem result = typeIds.get(type);
65
66        if (result == null) {
67            throw new IllegalArgumentException("not found: " + cst);
68        }
69
70        return result;
71    }
72
73    /**
74     * Writes the portion of the file header that refers to this instance.
75     *
76     * @param out {@code non-null;} where to write
77     */
78    public void writeHeaderPart(AnnotatedOutput out) {
79        throwIfNotPrepared();
80
81        int sz = typeIds.size();
82        int offset = (sz == 0) ? 0 : getFileOffset();
83
84        if (sz > 65536) {
85            throw new UnsupportedOperationException("too many type ids");
86        }
87
88        if (out.annotates()) {
89            out.annotate(4, "type_ids_size:   " + Hex.u4(sz));
90            out.annotate(4, "type_ids_off:    " + Hex.u4(offset));
91        }
92
93        out.writeInt(sz);
94        out.writeInt(offset);
95    }
96
97    /**
98     * Interns an element into this instance.
99     *
100     * @param type {@code non-null;} the type to intern
101     * @return {@code non-null;} the interned reference
102     */
103    public TypeIdItem intern(Type type) {
104        if (type == null) {
105            throw new NullPointerException("type == null");
106        }
107
108        throwIfPrepared();
109
110        TypeIdItem result = typeIds.get(type);
111
112        if (result == null) {
113            result = new TypeIdItem(new CstType(type));
114            typeIds.put(type, result);
115        }
116
117        return result;
118    }
119
120    /**
121     * Interns an element into this instance.
122     *
123     * @param type {@code non-null;} the type to intern
124     * @return {@code non-null;} the interned reference
125     */
126    public TypeIdItem intern(CstType type) {
127        if (type == null) {
128            throw new NullPointerException("type == null");
129        }
130
131        throwIfPrepared();
132
133        Type typePerSe = type.getClassType();
134        TypeIdItem result = typeIds.get(typePerSe);
135
136        if (result == null) {
137            result = new TypeIdItem(type);
138            typeIds.put(typePerSe, result);
139        }
140
141        return result;
142    }
143
144    /**
145     * Gets the index of the given type, which must have
146     * been added to this instance.
147     *
148     * @param type {@code non-null;} the type to look up
149     * @return {@code >= 0;} the reference's index
150     */
151    public int indexOf(Type type) {
152        if (type == null) {
153            throw new NullPointerException("type == null");
154        }
155
156        throwIfNotPrepared();
157
158        TypeIdItem item = typeIds.get(type);
159
160        if (item == null) {
161            throw new IllegalArgumentException("not found: " + type);
162        }
163
164        return item.getIndex();
165    }
166
167    /**
168     * Gets the index of the given type, which must have
169     * been added to this instance.
170     *
171     * @param type {@code non-null;} the type to look up
172     * @return {@code >= 0;} the reference's index
173     */
174    public int indexOf(CstType type) {
175        if (type == null) {
176            throw new NullPointerException("type == null");
177        }
178
179        return indexOf(type.getClassType());
180    }
181
182    /** {@inheritDoc} */
183    @Override
184    protected void orderItems() {
185        int idx = 0;
186
187        for (Object i : items()) {
188            ((TypeIdItem) i).setIndex(idx);
189            idx++;
190        }
191    }
192}
193