/* * [The "BSD licence"] * Copyright (c) 2010 Ben Gruver (JesusFreke) * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package org.jf.dexlib; import org.jf.dexlib.Util.AlignmentUtils; import org.jf.dexlib.Util.AnnotatedOutput; import org.jf.dexlib.Util.Input; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; public abstract class Section<T extends Item> { /** * A list of the items that this section contains. * If the section has been placed, this list should be in the order that the items * will written to the dex file */ protected final ArrayList<T> items; /** * A HashMap of the items in this section. This is used when interning items, to determine * if this section already has an item equivalent to the one that is being interned. * Both the key and the value should be the same object */ protected HashMap<T,T> uniqueItems = null; /** * The offset of this section within the <code>DexFile</code> */ protected int offset = 0; /** * The type of item that this section holds */ public final ItemType ItemType; /** * The <code>DexFile</code> that this section belongs to */ public final DexFile DexFile; /** * Create a new section * @param dexFile The <code>DexFile</code> that this section belongs to * @param itemType The itemType that this section will hold */ protected Section(DexFile dexFile, ItemType itemType) { this.DexFile = dexFile; items = new ArrayList<T>(); this.ItemType = itemType; } /** * Finalize the location of all items, and place them starting at the given offset * @param offset The offset where this section should be placed * @return the offset of the byte immediate after the last item in this section */ protected int placeAt(int offset) { if (items.size() > 0) { offset = AlignmentUtils.alignOffset(offset, ItemType.ItemAlignment); assert !DexFile.getInplace() || offset == this.offset; this.offset = offset; for (int i=0; i < items.size(); i++) { T item = items.get(i); assert item != null; offset = AlignmentUtils.alignOffset(offset, ItemType.ItemAlignment); offset = item.placeAt(offset, i); } } else { this.offset = 0; } return offset; } /** * Write the items to the given <code>AnnotatedOutput</code> * @param out the <code>AnnotatedOutput</code> object to write to */ protected void writeTo(AnnotatedOutput out) { out.annotate(0, " "); out.annotate(0, "-----------------------------"); out.annotate(0, this.ItemType.TypeName + " section"); out.annotate(0, "-----------------------------"); out.annotate(0, " "); for (Item item: items) { assert item!=null; out.alignTo(ItemType.ItemAlignment); item.writeTo(out); out.annotate(0, " "); } } /** * Read the specified number of items from the given <code>Input</code> object * @param size The number of items to read * @param in The <code>Input</code> object to read from * @param readContext a <code>ReadContext</code> object to hold information that is * only needed while reading in a file */ protected void readFrom(int size, Input in, ReadContext readContext) { //readItems() expects that the list will already be the correct size, so add null items //until we reach the specified size items.ensureCapacity(size); for (int i = items.size(); i < size; i++) { items.add(null); } in.alignTo(ItemType.ItemAlignment); offset = in.getCursor(); //call the subclass's method that actually reads in the items readItems(in, readContext); } /** * This method in the concrete item subclass should read in all the items from the given <code>Input</code> * object, using any pre-created items as applicable (i.e. items that were created prior to reading in the * section, by other items requesting items from this section that they reference by index/offset) * @param in the <code>Input</code> * @param readContext a <code>ReadContext</code> object to hold information that is * only needed while reading in a file */ protected abstract void readItems(Input in, ReadContext readContext); /** * Gets the offset where the first item in this section is placed * @return the ofset where the first item in this section is placed */ public int getOffset() { return offset; } /** * Gets a the items contained in this section as a read-only list * @return A read-only <code>List</code> object containing the items in this section */ public List<T> getItems() { return Collections.unmodifiableList(items); } /** * This method checks if an item that is equivalent to the given item has already been added. If found, * it returns that item. If not found, it adds the given item to this section and returns it. * @param item the item to intern * @return An item from this section that is equivalent to the given item. It may or may not be the same * as the item passed to this method. */ protected T intern(T item) { if (item == null) { return null; } T internedItem = getInternedItem(item); if (internedItem == null) { uniqueItems.put(item, item); items.add(item); return item; } return internedItem; } /** * Returns the interned item that is equivalent to the given item, or null * @param item the item to check * @return the interned item that is equivalent to the given item, or null */ protected T getInternedItem(T item) { if (uniqueItems == null) { buildInternedItemMap(); } return uniqueItems.get(item); } /** * Builds the interned item map from the items that are in this section */ private void buildInternedItemMap() { uniqueItems = new HashMap<T,T>(); for (T item: items) { assert item != null; uniqueItems.put(item, item); } } /** * Sorts the items in the section */ protected void sortSection() { Collections.sort(items); } }