blob: 98c2338900a3ff0c90e8bb44cbf74711eeeb78d9 [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 com.google.common.base.Preconditions;
import org.jf.dexlib.Util.AlignmentUtils;
import org.jf.dexlib.Util.AnnotatedOutput;
import org.jf.dexlib.Util.ExceptionWithContext;
import org.jf.dexlib.Util.Input;
public abstract class Item<T extends Item> implements Comparable<T> {
/**
* The offset of this item in the dex file, or -1 if not known
*/
protected int offset = -1;
/**
* The index of this item in the containing section, or -1 if not known
*/
protected int index = -1;
/**
* The DexFile that this item is associatedr with
*/
protected final DexFile dexFile;
/**
* The constructor that is used when reading in a <code>DexFile</code>
* @param dexFile the <code>DexFile</code> that this item is associated with
*/
protected Item(DexFile dexFile) {
assert dexFile != null;
this.dexFile = dexFile;
}
/**
* Read in the item from the given input stream, and initialize the index
* @param in the <code>Input</code> object to read from
* @param index the index within the containing section of the item being read in
* @param readContext a <code>ReadContext</code> object to hold information that is
* only needed while reading in a file
*/
protected void readFrom(Input in, int index, ReadContext readContext) {
try {
assert AlignmentUtils.isAligned(in.getCursor(), getItemType().ItemAlignment);
this.offset = in.getCursor();
this.index = index;
this.readItem(in, readContext);
} catch (Exception ex) {
throw addExceptionContext(ex);
}
}
/**
* Place the item at the given offset and index, and return the offset of the byte following this item
* @param offset The offset to place the item at
* @param index The index of the item within the containing section
* @return The offset of the byte following this item
*/
protected int placeAt(int offset, int index) {
try {
assert AlignmentUtils.isAligned(offset, getItemType().ItemAlignment);
assert !dexFile.getInplace() || (offset == this.offset && this.index == index);
this.offset = offset;
this.index = index;
return this.placeItem(offset);
} catch (Exception ex) {
throw addExceptionContext(ex);
}
}
/**
* Write and annotate this item to the output stream
* @param out The output stream to write and annotate to
*/
protected void writeTo(AnnotatedOutput out) {
try {
assert AlignmentUtils.isAligned(offset, getItemType().ItemAlignment);
//ensure that it is being written to the same offset where it was previously placed
assert out.getCursor() == offset;
if (out.annotates()) {
out.annotate(0, "[" + index + "] " + this.getItemType().TypeName);
}
out.indent();
writeItem(out);
out.deindent();
} catch (Exception ex) {
throw addExceptionContext(ex);
}
}
/**
* Returns a human readable form of this item
* @return a human readable form of this item
*/
public String toString() {
return getConciseIdentity();
}
/**
* The method in the concrete item subclass that actually reads in the data for the item
*
* The logic in this method can assume that the given Input object is valid and is
* aligned as neccessary.
*
* This method is for internal use only
* @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 abstract void readItem(Input in, ReadContext readContext);
/**
* The method should finalize the layout of the item and return the offset of the byte
* immediately following the item.
*
* The implementation of this method can assume that the offset argument has already been
* aligned based on the item's alignment requirements
*
* This method is for internal use only
* @param offset the (pre-aligned) offset to place the item at
* @return the size of the item, in bytes
*/
protected abstract int placeItem(int offset);
/**
* The method in the concrete item subclass that actually writes and annotates the data
* for the item.
*
* The logic in this method can assume that the given Output object is valid and is
* aligned as neccessary
*
* @param out The <code>AnnotatedOutput</code> object to write/annotate to
*/
protected abstract void writeItem(AnnotatedOutput out);
/**
* This method is called to add item specific context information to an exception, to identify the "current item"
* when the exception occured. It adds the value returned by <code>getConciseIdentity</code> as context for the
* exception
* @param ex The exception that occured
* @return A RuntimeException with additional details about the item added
*/
protected final RuntimeException addExceptionContext(Exception ex) {
return ExceptionWithContext.withContext(ex, getConciseIdentity());
}
/**
* @return An ItemType enum that represents the item type of this item
*/
public abstract ItemType getItemType();
/**
* @return A concise (human-readable) string value that conveys the identity of this item
*/
public abstract String getConciseIdentity();
/**
* Note that the item must have been placed before calling this method (See <code>DexFile.place()</code>)
* @return the offset in the dex file where this item is located
*/
public int getOffset() {
Preconditions.checkState(offset != -1,
"The offset is not set until the DexFile containing this item is placed.");
return offset;
}
/**
* Note that the item must have been placed before calling this method (See <code>DexFile.place()</code>)
* @return the index of this item within the item's containing section.
*/
public int getIndex() {
Preconditions.checkState(index != -1,
"The index is not set until the DexFile containing this item is placed.");
return index;
}
/**
* @return True if this item has been placed, otherwise False
*/
public boolean isPlaced() {
return offset != -1;
}
/**
* @return the <code>DexFile</code> that contains this item
*/
public DexFile getDexFile() {
return dexFile;
}
}