1/*
2 * [The "BSD licence"]
3 * Copyright (c) 2010 Ben Gruver (JesusFreke)
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 *    notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 * 3. The name of the author may not be used to endorse or promote products
15 *    derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29package org.jf.dexlib;
30
31import org.jf.dexlib.Util.AlignmentUtils;
32import org.jf.dexlib.Util.AnnotatedOutput;
33import org.jf.dexlib.Util.Input;
34
35import java.util.ArrayList;
36import java.util.Collections;
37import java.util.HashMap;
38import java.util.List;
39
40public abstract class Section<T extends Item> {
41    /**
42     * A list of the items that this section contains.
43     * If the section has been placed, this list should be in the order that the items
44     * will written to the dex file
45     */
46    protected final ArrayList<T> items;
47
48    /**
49     * A HashMap of the items in this section. This is used when interning items, to determine
50     * if this section already has an item equivalent to the one that is being interned.
51     * Both the key and the value should be the same object
52     */
53    protected HashMap<T,T> uniqueItems = null;
54
55    /**
56     * The offset of this section within the <code>DexFile</code>
57     */
58    protected int offset = 0;
59
60    /**
61     * The type of item that this section holds
62     */
63    public final ItemType ItemType;
64
65    /**
66     * The <code>DexFile</code> that this section belongs to
67     */
68    public final DexFile DexFile;
69
70    /**
71     * Create a new section
72     * @param dexFile The <code>DexFile</code> that this section belongs to
73     * @param itemType The itemType that this section will hold
74     */
75    protected Section(DexFile dexFile, ItemType itemType) {
76        this.DexFile = dexFile;
77        items = new ArrayList<T>();
78        this.ItemType = itemType;
79    }
80
81    /**
82     * Finalize the location of all items, and place them starting at the given offset
83     * @param offset The offset where this section should be placed
84     * @return the offset of the byte immediate after the last item in this section
85     */
86    protected int placeAt(int offset) {
87        if (items.size() > 0) {
88            offset = AlignmentUtils.alignOffset(offset, ItemType.ItemAlignment);
89            assert !DexFile.getInplace() || offset == this.offset;
90            this.offset = offset;
91
92            for (int i=0; i < items.size(); i++) {
93                T item = items.get(i);
94                assert item != null;
95                offset = AlignmentUtils.alignOffset(offset, ItemType.ItemAlignment);
96                offset = item.placeAt(offset, i);
97            }
98        } else {
99            this.offset = 0;
100        }
101
102        return offset;
103    }
104
105    /**
106     * Write the items to the given <code>AnnotatedOutput</code>
107     * @param out the <code>AnnotatedOutput</code> object to write to
108     */
109    protected void writeTo(AnnotatedOutput out) {
110        out.annotate(0, " ");
111        out.annotate(0, "-----------------------------");
112        out.annotate(0, this.ItemType.TypeName + " section");
113        out.annotate(0, "-----------------------------");
114        out.annotate(0, " ");
115
116        for (Item item: items) {
117            assert item!=null;
118            out.alignTo(ItemType.ItemAlignment);
119            item.writeTo(out);
120            out.annotate(0, " ");
121        }
122    }
123
124    /**
125     * Read the specified number of items from the given <code>Input</code> object
126     * @param size The number of items to read
127     * @param in The <code>Input</code> object to read from
128     * @param readContext a <code>ReadContext</code> object to hold information that is
129     * only needed while reading in a file
130     */
131    protected void readFrom(int size, Input in, ReadContext readContext) {
132        //readItems() expects that the list will already be the correct size, so add null items
133        //until we reach the specified size
134        items.ensureCapacity(size);
135        for (int i = items.size(); i < size; i++) {
136            items.add(null);
137        }
138
139        in.alignTo(ItemType.ItemAlignment);
140        offset = in.getCursor();
141
142        //call the subclass's method that actually reads in the items
143        readItems(in, readContext);
144    }
145
146    /**
147     * This method in the concrete item subclass should read in all the items from the given <code>Input</code>
148     * object, using any pre-created items as applicable (i.e. items that were created prior to reading in the
149     * section, by other items requesting items from this section that they reference by index/offset)
150     * @param in the <code>Input</code>
151     * @param readContext a <code>ReadContext</code> object to hold information that is
152     * only needed while reading in a file
153     */
154    protected abstract void readItems(Input in, ReadContext readContext);
155
156    /**
157     * Gets the offset where the first item in this section is placed
158     * @return the ofset where the first item in this section is placed
159     */
160    public int getOffset() {
161        return offset;
162    }
163
164    /**
165     * Gets a the items contained in this section as a read-only list
166     * @return A read-only <code>List</code> object containing the items in this section
167     */
168    public List<T> getItems() {
169        return Collections.unmodifiableList(items);
170    }
171
172    /**
173     * This method checks if an item that is equivalent to the given item has already been added. If found,
174     * it returns that item. If not found, it adds the given item to this section and returns it.
175     * @param item the item to intern
176     * @return An item from this section that is equivalent to the given item. It may or may not be the same
177     * as the item passed to this method.
178     */
179    protected T intern(T item) {
180        if (item == null) {
181            return null;
182        }
183        T internedItem = getInternedItem(item);
184        if (internedItem == null) {
185            uniqueItems.put(item, item);
186            items.add(item);
187            return item;
188        }
189        return internedItem;
190    }
191
192    /**
193     * Returns the interned item that is equivalent to the given item, or null
194     * @param item the item to check
195     * @return the interned item that is equivalent to the given item, or null
196     */
197    protected T getInternedItem(T item) {
198        if (uniqueItems == null) {
199            buildInternedItemMap();
200        }
201        return uniqueItems.get(item);
202    }
203
204    /**
205     * Builds the interned item map from the items that are in this section
206     */
207    private void buildInternedItemMap() {
208        uniqueItems = new HashMap<T,T>();
209        for (T item: items) {
210            assert item != null;
211            uniqueItems.put(item, item);
212        }
213    }
214
215    /**
216     * Sorts the items in the section
217     */
218    protected void sortSection() {
219        Collections.sort(items);
220    }
221}