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 29 package org.jf.dexlib; 30 31 import com.google.common.base.Preconditions; 32 import org.jf.dexlib.Util.AlignmentUtils; 33 import org.jf.dexlib.Util.AnnotatedOutput; 34 import org.jf.dexlib.Util.ExceptionWithContext; 35 import org.jf.dexlib.Util.Input; 36 37 public abstract class Item<T extends Item> implements Comparable<T> { 38 /** 39 * The offset of this item in the dex file, or -1 if not known 40 */ 41 protected int offset = -1; 42 43 /** 44 * The index of this item in the containing section, or -1 if not known 45 */ 46 protected int index = -1; 47 48 /** 49 * The DexFile that this item is associatedr with 50 */ 51 protected final DexFile dexFile; 52 53 /** 54 * The constructor that is used when reading in a <code>DexFile</code> 55 * @param dexFile the <code>DexFile</code> that this item is associated with 56 */ 57 protected Item(DexFile dexFile) { 58 assert dexFile != null; 59 60 this.dexFile = dexFile; 61 } 62 63 /** 64 * Read in the item from the given input stream, and initialize the index 65 * @param in the <code>Input</code> object to read from 66 * @param index the index within the containing section of the item being read in 67 * @param readContext a <code>ReadContext</code> object to hold information that is 68 * only needed while reading in a file 69 */ 70 protected void readFrom(Input in, int index, ReadContext readContext) { 71 try { 72 assert AlignmentUtils.isAligned(in.getCursor(), getItemType().ItemAlignment); 73 74 this.offset = in.getCursor(); 75 this.index = index; 76 77 this.readItem(in, readContext); 78 } catch (Exception ex) { 79 throw addExceptionContext(ex); 80 } 81 } 82 83 /** 84 * Place the item at the given offset and index, and return the offset of the byte following this item 85 * @param offset The offset to place the item at 86 * @param index The index of the item within the containing section 87 * @return The offset of the byte following this item 88 */ 89 protected int placeAt(int offset, int index) { 90 try { 91 assert AlignmentUtils.isAligned(offset, getItemType().ItemAlignment); 92 assert !dexFile.getInplace() || (offset == this.offset && this.index == index); 93 94 this.offset = offset; 95 this.index = index; 96 return this.placeItem(offset); 97 } catch (Exception ex) { 98 throw addExceptionContext(ex); 99 } 100 } 101 102 /** 103 * Write and annotate this item to the output stream 104 * @param out The output stream to write and annotate to 105 */ 106 protected void writeTo(AnnotatedOutput out) { 107 try { 108 assert AlignmentUtils.isAligned(offset, getItemType().ItemAlignment); 109 //ensure that it is being written to the same offset where it was previously placed 110 assert out.getCursor() == offset; 111 112 if (out.annotates()) { 113 out.annotate(0, "[" + index + "] " + this.getItemType().TypeName); 114 } 115 116 out.indent(); 117 writeItem(out); 118 out.deindent(); 119 } catch (Exception ex) { 120 throw addExceptionContext(ex); 121 } 122 } 123 124 /** 125 * Returns a human readable form of this item 126 * @return a human readable form of this item 127 */ 128 public String toString() { 129 return getConciseIdentity(); 130 } 131 132 /** 133 * The method in the concrete item subclass that actually reads in the data for the item 134 * 135 * The logic in this method can assume that the given Input object is valid and is 136 * aligned as neccessary. 137 * 138 * This method is for internal use only 139 * @param in the <code>Input</code> object to read from 140 * @param readContext a <code>ReadContext</code> object to hold information that is 141 * only needed while reading in a file 142 */ 143 protected abstract void readItem(Input in, ReadContext readContext); 144 145 /** 146 * The method should finalize the layout of the item and return the offset of the byte 147 * immediately following the item. 148 * 149 * The implementation of this method can assume that the offset argument has already been 150 * aligned based on the item's alignment requirements 151 * 152 * This method is for internal use only 153 * @param offset the (pre-aligned) offset to place the item at 154 * @return the size of the item, in bytes 155 */ 156 protected abstract int placeItem(int offset); 157 158 /** 159 * The method in the concrete item subclass that actually writes and annotates the data 160 * for the item. 161 * 162 * The logic in this method can assume that the given Output object is valid and is 163 * aligned as neccessary 164 * 165 * @param out The <code>AnnotatedOutput</code> object to write/annotate to 166 */ 167 protected abstract void writeItem(AnnotatedOutput out); 168 169 /** 170 * This method is called to add item specific context information to an exception, to identify the "current item" 171 * when the exception occured. It adds the value returned by <code>getConciseIdentity</code> as context for the 172 * exception 173 * @param ex The exception that occured 174 * @return A RuntimeException with additional details about the item added 175 */ 176 protected final RuntimeException addExceptionContext(Exception ex) { 177 return ExceptionWithContext.withContext(ex, getConciseIdentity()); 178 } 179 180 /** 181 * @return An ItemType enum that represents the item type of this item 182 */ 183 public abstract ItemType getItemType(); 184 185 /** 186 * @return A concise (human-readable) string value that conveys the identity of this item 187 */ 188 public abstract String getConciseIdentity(); 189 190 191 /** 192 * Note that the item must have been placed before calling this method (See <code>DexFile.place()</code>) 193 * @return the offset in the dex file where this item is located 194 */ 195 public int getOffset() { 196 Preconditions.checkState(offset != -1, 197 "The offset is not set until the DexFile containing this item is placed."); 198 return offset; 199 } 200 201 /** 202 * Note that the item must have been placed before calling this method (See <code>DexFile.place()</code>) 203 * @return the index of this item within the item's containing section. 204 */ 205 public int getIndex() { 206 Preconditions.checkState(index != -1, 207 "The index is not set until the DexFile containing this item is placed."); 208 return index; 209 } 210 211 /** 212 * @return True if this item has been placed, otherwise False 213 */ 214 public boolean isPlaced() { 215 return offset != -1; 216 } 217 218 /** 219 * @return the <code>DexFile</code> that contains this item 220 */ 221 public DexFile getDexFile() { 222 return dexFile; 223 } 224 } 225