OffsettedItem.java revision f6c387128427e121477c1b32ad35cdcaa5101ba3
1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.dx.dex.file; 18 19import com.android.dx.util.AnnotatedOutput; 20import com.android.dx.util.ExceptionWithContext; 21 22/** 23 * An item in a Dalvik file which is referenced by absolute offset. 24 */ 25public abstract class OffsettedItem extends Item 26 implements Comparable<OffsettedItem> { 27 /** > 0; alignment requirement */ 28 private final int alignment; 29 30 /** >= -1; the size of this instance when written, in bytes, or 31 * <code>-1</code> if not yet known */ 32 private int writeSize; 33 34 /** 35 * null-ok; section the item was added to, or <code>null</code> if 36 * not yet added 37 */ 38 private Section addedTo; 39 40 /** 41 * >= -1; assigned offset of the item from the start of its section, 42 * or <code>-1</code> if not yet assigned 43 */ 44 private int offset; 45 46 /** 47 * Gets the absolute offset of the given item, returning <code>0</code> 48 * if handed <code>null</code>. 49 * 50 * @param item null-ok; the item in question 51 * @return >= 0; the item's absolute offset, or <code>0</code> 52 * if <code>item == null</code> 53 */ 54 public static int getAbsoluteOffsetOr0(OffsettedItem item) { 55 if (item == null) { 56 return 0; 57 } 58 59 return item.getAbsoluteOffset(); 60 } 61 62 /** 63 * Constructs an instance. The offset is initially unassigned. 64 * 65 * @param alignment > 0; output alignment requirement; must be a 66 * power of 2 67 * @param writeSize >= -1; the size of this instance when written, 68 * in bytes, or <code>-1</code> if not immediately known 69 */ 70 public OffsettedItem(int alignment, int writeSize) { 71 Section.validateAlignment(alignment); 72 73 if (writeSize < -1) { 74 throw new IllegalArgumentException("writeSize < -1"); 75 } 76 77 this.alignment = alignment; 78 this.writeSize = writeSize; 79 this.addedTo = null; 80 this.offset = -1; 81 } 82 83 /** 84 * {@inheritDoc} 85 * 86 * Comparisons for this class are defined to be type-major (if the 87 * types don't match then the objects are not equal), with 88 * {@link #compareTo0} deciding same-type comparisons. 89 */ 90 @Override 91 public final boolean equals(Object other) { 92 if (this == other) { 93 return true; 94 } 95 96 OffsettedItem otherItem = (OffsettedItem) other; 97 ItemType thisType = itemType(); 98 ItemType otherType = otherItem.itemType(); 99 100 if (thisType != otherType) { 101 return false; 102 } 103 104 return (compareTo0(otherItem) == 0); 105 } 106 107 /** 108 * {@inheritDoc} 109 * 110 * Comparisons for this class are defined to be class-major (if the 111 * classes don't match then the objects are not equal), with 112 * {@link #compareTo0} deciding same-class comparisons. 113 */ 114 public final int compareTo(OffsettedItem other) { 115 if (this == other) { 116 return 0; 117 } 118 119 ItemType thisType = itemType(); 120 ItemType otherType = other.itemType(); 121 122 if (thisType != otherType) { 123 return thisType.compareTo(otherType); 124 } 125 126 return compareTo0(other); 127 } 128 129 /** 130 * Sets the write size of this item. This may only be called once 131 * per instance, and only if the size was unknown upon instance 132 * creation. 133 * 134 * @param writeSize > 0; the write size, in bytes 135 */ 136 public final void setWriteSize(int writeSize) { 137 if (writeSize < 0) { 138 throw new IllegalArgumentException("writeSize < 0"); 139 } 140 141 if (this.writeSize >= 0) { 142 throw new UnsupportedOperationException("writeSize already set"); 143 } 144 145 this.writeSize = writeSize; 146 } 147 148 /** {@inheritDoc} 149 * 150 * @throws UnsupportedOperationException thrown if the write size 151 * is not yet known 152 */ 153 @Override 154 public final int writeSize() { 155 if (writeSize < 0) { 156 throw new UnsupportedOperationException("writeSize is unknown"); 157 } 158 159 return writeSize; 160 } 161 162 /** {@inheritDoc} */ 163 @Override 164 public final void writeTo(DexFile file, AnnotatedOutput out) { 165 out.alignTo(alignment); 166 167 try { 168 if (writeSize < 0) { 169 throw new UnsupportedOperationException( 170 "writeSize is unknown"); 171 } 172 out.assertCursor(getAbsoluteOffset()); 173 } catch (RuntimeException ex) { 174 throw ExceptionWithContext.withContext(ex, 175 "...while writing " + this); 176 } 177 178 writeTo0(file, out); 179 } 180 181 /** 182 * Gets the relative item offset. The offset is from the start of 183 * the section which the instance was written to. 184 * 185 * @return >= 0; the offset 186 * @throws RuntimeException thrown if the offset is not yet known 187 */ 188 public final int getRelativeOffset() { 189 if (offset < 0) { 190 throw new RuntimeException("offset not yet known"); 191 } 192 193 return offset; 194 } 195 196 /** 197 * Gets the absolute item offset. The offset is from the start of 198 * the file which the instance was written to. 199 * 200 * @return >= 0; the offset 201 * @throws RuntimeException thrown if the offset is not yet known 202 */ 203 public final int getAbsoluteOffset() { 204 if (offset < 0) { 205 throw new RuntimeException("offset not yet known"); 206 } 207 208 return addedTo.getAbsoluteOffset(offset); 209 } 210 211 /** 212 * Indicates that this item has been added to the given section at 213 * the given offset. It is only valid to call this method once per 214 * instance. 215 * 216 * @param addedTo non-null; the section this instance has been added to 217 * @param offset >= 0; the desired offset from the start of the 218 * section where this instance was placed 219 * @return >= 0; the offset that this instance should be placed at 220 * in order to meet its alignment constraint 221 */ 222 public final int place(Section addedTo, int offset) { 223 if (addedTo == null) { 224 throw new NullPointerException("addedTo == null"); 225 } 226 227 if (offset < 0) { 228 throw new IllegalArgumentException("offset < 0"); 229 } 230 231 if (this.addedTo != null) { 232 throw new RuntimeException("already written"); 233 } 234 235 int mask = alignment - 1; 236 offset = (offset + mask) & ~mask; 237 238 this.addedTo = addedTo; 239 this.offset = offset; 240 241 place0(addedTo, offset); 242 243 return offset; 244 } 245 246 /** 247 * Gets the alignment requirement of this instance. An instance should 248 * only be written when so aligned. 249 * 250 * @return > 0; the alignment requirement; must be a power of 2 251 */ 252 public final int getAlignment() { 253 return alignment; 254 } 255 256 /** 257 * Gets the absolute offset of this item as a string, suitable for 258 * including in annotations. 259 * 260 * @return non-null; the offset string 261 */ 262 public final String offsetString() { 263 return '[' + Integer.toHexString(getAbsoluteOffset()) + ']'; 264 } 265 266 /** 267 * Gets a short human-readable string representing this instance. 268 * 269 * @return non-null; the human form 270 */ 271 public abstract String toHuman(); 272 273 /** 274 * Compares this instance to another which is guaranteed to be of 275 * the same class. The default implementation of this method is to 276 * throw an exception (unsupported operation). If a particular 277 * class needs to actually sort, then it should override this 278 * method. 279 * 280 * @param other non-null; instance to compare to 281 * @return <code>-1</code>, <code>0</code>, or <code>1</code>, depending 282 * on the sort order of this instance and the other 283 */ 284 protected int compareTo0(OffsettedItem other) { 285 throw new UnsupportedOperationException("unsupported"); 286 } 287 288 /** 289 * Does additional work required when placing an instance. The 290 * default implementation of this method is a no-op. If a 291 * particular class needs to do something special, then it should 292 * override this method. In particular, if this instance did not 293 * know its write size up-front, then this method is responsible 294 * for setting it. 295 * 296 * @param addedTo non-null; the section this instance has been added to 297 * @param offset >= 0; the offset from the start of the 298 * section where this instance was placed 299 */ 300 protected void place0(Section addedTo, int offset) { 301 // This space intentionally left blank. 302 } 303 304 /** 305 * Performs the actual write of the contents of this instance to 306 * the given data section. This is called by {@link #writeTo}, 307 * which will have taken care of ensuring alignment. 308 * 309 * @param file non-null; the file to use for reference 310 * @param out non-null; where to write to 311 */ 312 protected abstract void writeTo0(DexFile file, AnnotatedOutput out); 313} 314