blob: 0c20a71951bb117414ae0edc6bf35fa578b7a0f9 [file] [log] [blame]
/*
* [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);
}
}