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}