AnnotationDirectoryItem.java revision 928790f2939e0eec7ab2e4653a19c6c27a113634
1/* 2 * [The "BSD licence"] 3 * Copyright (c) 2009 Ben Gruver 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 29package org.jf.dexlib; 30 31import org.jf.dexlib.Util.ExceptionWithContext; 32import org.jf.dexlib.Util.Input; 33import org.jf.dexlib.Util.AnnotatedOutput; 34 35import java.util.Collections; 36import java.util.List; 37 38public class AnnotationDirectoryItem extends Item<AnnotationDirectoryItem> { 39 private AnnotationSetItem classAnnotations; 40 41 private FieldIdItem[] fieldAnnotationFields; 42 private AnnotationSetItem[] fieldAnnotations; 43 44 private MethodIdItem[] methodAnnotationMethods; 45 private AnnotationSetItem[] methodAnnotations; 46 47 private MethodIdItem[] parameterAnnotationMethods; 48 private AnnotationSetRefList[] parameterAnnotations; 49 50 /** 51 * typically each AnnotationDirectoryItem will have a distinct parent. The only case that isn't true is when 52 * the AnnotationDirectoryItem *only* contains class annotations, with no other type of annotation. In that 53 * case, the same AnnotationDirectoryItem could be referenced from multiple classes. 54 * This isn't a problem though, because this field is only used in compareTo to determine the sort order, 55 * which handles it as a special case 56 */ 57 private ClassDefItem parent = null; 58 59 /** 60 * Creates a new uninitialized <code>AnnotationDirectoryItem</code> 61 * @param dexFile The <code>DexFile</code> that this item belongs to 62 */ 63 protected AnnotationDirectoryItem(DexFile dexFile) { 64 super(dexFile); 65 } 66 67 /** 68 * Creates a new <code>AnnotationDirectoryItem</code> with the given values 69 * @param dexFile The <code>DexFile</code> that this item belongs to 70 * @param classAnnotations The annotations associated with the overall class 71 * @param fieldAnnotationFields An array of <code>FieldIdItem</code> objects that the annotations in 72 * <code>fieldAnnotations</code> are associated with 73 * @param fieldAnnotations An array of <code>AnnotationSetItem</code> objects that contain the annotations for the 74 * fields in <code>fieldAnnotationFields</code> 75 * @param methodAnnotationMethods An array of <code>MethodIdItem</code> objects that the annotations in 76 * <code>methodAnnotations</code> are associated with 77 * @param methodAnnotations An array of <code>AnnotationSetItem</code> objects that contain the annotations for the 78 * methods in <code>methodAnnotationMethods</code> 79 * @param parameterAnnotationMethods An array of <code>MethodIdItem</code> objects that the annotations in 80 * <code>parameterAnnotations</code> are associated with 81 * @param parameterAnnotations An array of <code>AnnotationSetRefList</code> objects that contain the parameter 82 * annotations for the methods in <code>parameterAnnotationMethods</code> 83 */ 84 private AnnotationDirectoryItem(DexFile dexFile, AnnotationSetItem classAnnotations, 85 FieldIdItem[] fieldAnnotationFields, AnnotationSetItem[] fieldAnnotations, 86 MethodIdItem[] methodAnnotationMethods, AnnotationSetItem[] methodAnnotations, 87 MethodIdItem[] parameterAnnotationMethods, 88 AnnotationSetRefList[] parameterAnnotations) { 89 super(dexFile); 90 this.classAnnotations = classAnnotations; 91 this.fieldAnnotationFields = fieldAnnotationFields; 92 this.fieldAnnotations = fieldAnnotations; 93 this.methodAnnotationMethods = methodAnnotationMethods; 94 this.methodAnnotations = methodAnnotations; 95 this.parameterAnnotationMethods = parameterAnnotationMethods; 96 this.parameterAnnotations = parameterAnnotations; 97 } 98 99 /** 100 * Returns an <code>AnnotationDirectoryItem</code> for the given values, and that has been interned into the given 101 * <code>DexFile</code> 102 * @param dexFile The <code>DexFile</code> that this item belongs to 103 * @param classAnnotations The annotations associated with the class 104 * @param fieldAnnotations A list of <code>FieldAnnotation</code> objects containing the field annotations 105 * @param methodAnnotations A list of <code>MethodAnnotation</code> objects containing the method annotations 106 * @param parameterAnnotations A list of <code>ParameterAnnotation</code> objects containin the parameter 107 * annotations 108 * @return an <code>AnnotationItem</code> for the given values, and that has been interned into the given 109 * <code>DexFile</code> 110 */ 111 public static AnnotationDirectoryItem internAnnotationDirectoryItem(DexFile dexFile, 112 AnnotationSetItem classAnnotations, 113 List<FieldAnnotation> fieldAnnotations, 114 List<MethodAnnotation> methodAnnotations, 115 List<ParameterAnnotation> parameterAnnotations) { 116 FieldIdItem[] fieldAnnotationFields = null; 117 AnnotationSetItem[] fieldAnnotationsArray = null; 118 MethodIdItem[] methodAnnotationMethods = null; 119 AnnotationSetItem[] methodAnnotationsArray = null; 120 MethodIdItem[] parameterAnnotationMethods = null; 121 AnnotationSetRefList[] parameterAnnotationsArray = null; 122 123 if (fieldAnnotations != null && fieldAnnotations.size() > 0) { 124 fieldAnnotationFields = new FieldIdItem[fieldAnnotations.size()]; 125 fieldAnnotationsArray = new AnnotationSetItem[fieldAnnotations.size()]; 126 127 Collections.sort(fieldAnnotations); 128 129 int index = 0; 130 for (FieldAnnotation fieldAnnotation: fieldAnnotations) { 131 fieldAnnotationFields[index] = fieldAnnotation.field; 132 fieldAnnotationsArray[index++] = fieldAnnotation.annotationSet; 133 } 134 } 135 136 if (methodAnnotations != null && methodAnnotations.size() > 0) { 137 methodAnnotationMethods = new MethodIdItem[methodAnnotations.size()]; 138 methodAnnotationsArray = new AnnotationSetItem[methodAnnotations.size()]; 139 140 Collections.sort(methodAnnotations); 141 142 int index = 0; 143 for (MethodAnnotation methodAnnotation: methodAnnotations) { 144 methodAnnotationMethods[index] = methodAnnotation.method; 145 methodAnnotationsArray[index++] = methodAnnotation.annotationSet; 146 } 147 } 148 149 if (parameterAnnotations != null && parameterAnnotations.size() > 0) { 150 parameterAnnotationMethods = new MethodIdItem[parameterAnnotations.size()]; 151 parameterAnnotationsArray = new AnnotationSetRefList[parameterAnnotations.size()]; 152 153 Collections.sort(parameterAnnotations); 154 155 int index = 0; 156 for (ParameterAnnotation parameterAnnotation: parameterAnnotations) { 157 parameterAnnotationMethods[index] = parameterAnnotation.method; 158 parameterAnnotationsArray[index++] = parameterAnnotation.annotationSet; 159 } 160 } 161 162 AnnotationDirectoryItem annotationDirectoryItem = new AnnotationDirectoryItem(dexFile, classAnnotations, 163 fieldAnnotationFields, fieldAnnotationsArray, methodAnnotationMethods, methodAnnotationsArray, 164 parameterAnnotationMethods, parameterAnnotationsArray); 165 return dexFile.AnnotationDirectoriesSection.intern(annotationDirectoryItem); 166 } 167 168 /** {@inheritDoc} */ 169 protected void readItem(Input in, ReadContext readContext) { 170 classAnnotations = (AnnotationSetItem)readContext.getOptionalOffsettedItemByOffset( 171 ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt()); 172 fieldAnnotationFields = new FieldIdItem[in.readInt()]; 173 fieldAnnotations = new AnnotationSetItem[fieldAnnotationFields.length]; 174 175 methodAnnotationMethods = new MethodIdItem[in.readInt()]; 176 methodAnnotations = new AnnotationSetItem[methodAnnotationMethods.length]; 177 178 parameterAnnotationMethods = new MethodIdItem[in.readInt()]; 179 parameterAnnotations = new AnnotationSetRefList[parameterAnnotationMethods.length]; 180 181 for (int i=0; i<fieldAnnotations.length; i++) { 182 try { 183 fieldAnnotationFields[i] = dexFile.FieldIdsSection.getItemByIndex(in.readInt()); 184 fieldAnnotations[i] = (AnnotationSetItem)readContext.getOffsettedItemByOffset( 185 ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt()); 186 } catch (Exception ex) { 187 throw ExceptionWithContext.withContext(ex, 188 "Error occured while reading FieldAnnotation at index " + i); 189 } 190 } 191 192 for (int i=0; i<methodAnnotations.length; i++) { 193 try { 194 methodAnnotationMethods[i] = dexFile.MethodIdsSection.getItemByIndex(in.readInt()); 195 methodAnnotations[i] = (AnnotationSetItem)readContext.getOffsettedItemByOffset( 196 ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt()); 197 } catch (Exception ex) { 198 throw ExceptionWithContext.withContext(ex, 199 "Error occured while reading MethodAnnotation at index " + i); 200 } 201 } 202 203 for (int i=0; i<parameterAnnotations.length; i++) { 204 try { 205 parameterAnnotationMethods[i] = dexFile.MethodIdsSection.getItemByIndex(in.readInt()); 206 parameterAnnotations[i] = (AnnotationSetRefList)readContext.getOffsettedItemByOffset( 207 ItemType.TYPE_ANNOTATION_SET_REF_LIST, in.readInt()); 208 } catch (Exception ex) { 209 throw ExceptionWithContext.withContext(ex, 210 "Error occured while reading ParameterAnnotation at index " + i); 211 } 212 } 213 } 214 215 /** {@inheritDoc} */ 216 protected int placeItem(int offset) { 217 return offset + 16 + ( 218 (fieldAnnotations==null?0:fieldAnnotations.length) + 219 (methodAnnotations==null?0:methodAnnotations.length) + 220 (parameterAnnotations==null?0:parameterAnnotations.length)) * 8; 221 } 222 223 /** {@inheritDoc} */ 224 protected void writeItem(AnnotatedOutput out) { 225 if (out.annotates()) { 226 if (!isInternable() && parent != null) { 227 out.annotate(0, parent.getClassType().getTypeDescriptor()); 228 } 229 if (classAnnotations != null) { 230 out.annotate(4, "class_annotations_off: 0x" + Integer.toHexString(classAnnotations.getOffset())); 231 } else { 232 out.annotate(4, "class_annotations_off:"); 233 } 234 235 int length = fieldAnnotations==null?0:fieldAnnotations.length; 236 out.annotate(4, "annotated_fields_size: 0x" + Integer.toHexString(length) + " (" + 237 length + ")"); 238 length = methodAnnotations==null?0:methodAnnotations.length; 239 out.annotate(4, "annotated_methods_size: 0x" + Integer.toHexString(length) + " (" + 240 length + ")"); 241 length = parameterAnnotations==null?0:parameterAnnotations.length; 242 out.annotate(4, "annotated_parameters_size: 0x" + Integer.toHexString(length) + " (" + 243 length + ")"); 244 245 int index; 246 if (fieldAnnotations != null) { 247 index = 0; 248 for (int i=0; i<fieldAnnotations.length; i++) { 249 out.annotate(0, "[" + index++ + "] field_annotation"); 250 251 out.indent(); 252 out.annotate(4, "field: " + fieldAnnotationFields[i].getFieldName().getStringValue() + ":" + 253 fieldAnnotationFields[i].getFieldType().getTypeDescriptor()); 254 out.annotate(4, "annotations_off: 0x" + Integer.toHexString(fieldAnnotations[i].getOffset())); 255 out.deindent(); 256 } 257 } 258 259 if (methodAnnotations != null) { 260 index = 0; 261 for (int i=0; i<methodAnnotations.length; i++) { 262 out.annotate(0, "[" + index++ + "] method_annotation"); 263 out.indent(); 264 out.annotate(4, "method: " + methodAnnotationMethods[i].getMethodString()); 265 out.annotate(4, "annotations_off: 0x" + Integer.toHexString(methodAnnotations[i].getOffset())); 266 out.deindent(); 267 } 268 } 269 270 if (parameterAnnotations != null) { 271 index = 0; 272 for (int i=0; i<parameterAnnotations.length; i++) { 273 out.annotate(0, "[" + index++ + "] parameter_annotation"); 274 out.indent(); 275 out.annotate(4, "method: " + parameterAnnotationMethods[i].getMethodString()); 276 out.annotate(4, "annotations_off: 0x" + Integer.toHexString(parameterAnnotations[i].getOffset())); 277 } 278 } 279 } 280 281 out.writeInt(classAnnotations==null?0:classAnnotations.getOffset()); 282 out.writeInt(fieldAnnotations==null?0:fieldAnnotations.length); 283 out.writeInt(methodAnnotations==null?0:methodAnnotations.length); 284 out.writeInt(parameterAnnotations==null?0:parameterAnnotations.length); 285 286 if (fieldAnnotations != null) { 287 for (int i=0; i<fieldAnnotations.length; i++) { 288 out.writeInt(fieldAnnotationFields[i].getIndex()); 289 out.writeInt(fieldAnnotations[i].getOffset()); 290 } 291 } 292 293 if (methodAnnotations != null) { 294 for (int i=0; i<methodAnnotations.length; i++) { 295 out.writeInt(methodAnnotationMethods[i].getIndex()); 296 out.writeInt(methodAnnotations[i].getOffset()); 297 } 298 } 299 300 if (parameterAnnotations != null) { 301 for (int i=0; i<parameterAnnotations.length; i++) { 302 out.writeInt(parameterAnnotationMethods[i].getIndex()); 303 out.writeInt(parameterAnnotations[i].getOffset()); 304 } 305 } 306 } 307 308 /** {@inheritDoc} */ 309 public ItemType getItemType() { 310 return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM; 311 } 312 313 /** {@inheritDoc} */ 314 public String getConciseIdentity() { 315 if (parent == null) { 316 return "annotation_directory_item @0x" + Integer.toHexString(getOffset()); 317 } 318 return "annotation_directory_item @0x" + Integer.toHexString(getOffset()) + " (" + parent.getClassType() + ")"; 319 } 320 321 /** {@inheritDoc} */ 322 public int compareTo(AnnotationDirectoryItem o) { 323 if (!isInternable()) { 324 if (!o.isInternable()) { 325 return parent.compareTo(o.parent); 326 } 327 return -1; 328 } 329 330 if (!o.isInternable()) { 331 return 1; 332 } 333 334 return classAnnotations.compareTo(o.classAnnotations); 335 } 336 337 /** 338 * @return The annotations associated with the class 339 */ 340 public AnnotationSetItem getClassAnnotations() { 341 return classAnnotations; 342 } 343 344 /** 345 * Iterates over the field annotations, calling delegate.processFieldAnnotations for each 346 * @param delegate the delegate to call 347 */ 348 public void iterateFieldAnnotations(FieldAnnotationIteratorDelegate delegate) { 349 for (int i=0; i<fieldAnnotationFields.length; i++) { 350 try { 351 delegate.processFieldAnnotations(fieldAnnotationFields[i], fieldAnnotations[i]); 352 } catch (Exception ex) { 353 throw addExceptionContext(ExceptionWithContext.withContext(ex, 354 "Error occured while processing field annotations for field: " + 355 fieldAnnotationFields[i].getFieldString())); 356 } 357 } 358 } 359 360 public static interface FieldAnnotationIteratorDelegate { 361 void processFieldAnnotations(FieldIdItem field, AnnotationSetItem fieldAnnotations); 362 } 363 364 /** 365 * @return the number of field annotations in this <code>AnnotationDirectoryItem</code> 366 */ 367 public int getFieldAnnotationCount() { 368 return fieldAnnotationFields.length; 369 } 370 371 /** 372 * Iterates over the method annotations, calling delegate.processMethodAnnotations for each 373 * @param delegate the delegate to call 374 */ 375 public void iterateMethodAnnotations(MethodAnnotationIteratorDelegate delegate) { 376 for (int i=0; i<methodAnnotationMethods.length; i++) { 377 try { 378 delegate.processMethodAnnotations(methodAnnotationMethods[i], methodAnnotations[i]); 379 } catch (Exception ex) { 380 throw addExceptionContext(ExceptionWithContext.withContext(ex, 381 "Error occured while processing method annotations for method: " + 382 methodAnnotationMethods[i].getMethodString())); 383 } 384 } 385 } 386 387 public static interface MethodAnnotationIteratorDelegate { 388 void processMethodAnnotations(MethodIdItem method, AnnotationSetItem methodAnnotations); 389 } 390 391 /** 392 * @return the number of method annotations in this <code>AnnotationDirectoryItem</code> 393 */ 394 public int getMethodAnnotationCount() { 395 return methodAnnotationMethods.length; 396 } 397 398 /** 399 * Iterates over the parameter annotations, calling delegate.processParameterAnnotations for each 400 * @param delegate the delegate to call 401 */ 402 public void iterateParameterAnnotations(ParameterAnnotationIteratorDelegate delegate) { 403 for (int i=0; i<parameterAnnotationMethods.length; i++) { 404 try { 405 delegate.processParameterAnnotations(parameterAnnotationMethods[i], parameterAnnotations[i]); 406 } catch (Exception ex) { 407 throw addExceptionContext(ExceptionWithContext.withContext(ex, 408 "Error occured while processing parameter annotations for method: " + 409 parameterAnnotationMethods[i].getMethodString())); 410 } 411 } 412 } 413 414 public static interface ParameterAnnotationIteratorDelegate { 415 void processParameterAnnotations(MethodIdItem method, AnnotationSetRefList parameterAnnotations); 416 } 417 418 /** 419 * @return the number of parameter annotations in this <code>AnnotationDirectoryItem</code> 420 */ 421 public int getParameterAnnotationCount() { 422 return parameterAnnotationMethods.length; 423 } 424 425 /** 426 * @return true if this <code>AnnotationDirectoryItem</code> is internable. It is only internable if it has 427 * only class annotations, but no field, method or parameter annotations 428 */ 429 private boolean isInternable() { 430 return classAnnotations != null && 431 (fieldAnnotations == null || fieldAnnotations.length == 0) && 432 (methodAnnotations == null || methodAnnotations.length == 0) && 433 (parameterAnnotations == null || parameterAnnotations.length == 0); 434 } 435 436 /** 437 * Sets the <code>ClassDefItem</code> that this <code>AnnotationDirectoryItem</code> is associated with. 438 * This is only applicable if this AnnotationDirectoryItem contains only class annotations, and no field, method 439 * or parameter annotations. 440 * @param classDefItem the <code>ClassDefItem</code> that this <code>AnnotationDirectoryItem</code> is associated 441 * with 442 */ 443 protected void setParent(ClassDefItem classDefItem) { 444 this.parent = classDefItem; 445 } 446 447 @Override 448 public int hashCode() { 449 //an instance is only internable if it has only class annotations, but 450 //no other type of annotation 451 if (!isInternable()) { 452 return super.hashCode(); 453 } 454 return classAnnotations.hashCode(); 455 } 456 457 @Override 458 public boolean equals(Object o) { 459 if (this==o) { 460 return true; 461 } 462 if (o==null || !this.getClass().equals(o.getClass())) { 463 return false; 464 } 465 466 AnnotationDirectoryItem other = (AnnotationDirectoryItem)o; 467 return (this.compareTo(other) == 0); 468 } 469 470 public static class FieldAnnotation implements Comparable<FieldAnnotation> { 471 public final FieldIdItem field; 472 public final AnnotationSetItem annotationSet; 473 474 public FieldAnnotation(FieldIdItem field, AnnotationSetItem annotationSet) { 475 this.field = field; 476 this.annotationSet = annotationSet; 477 } 478 479 public int compareTo(FieldAnnotation other) { 480 return field.compareTo(other.field); 481 } 482 } 483 484 public static class MethodAnnotation implements Comparable<MethodAnnotation> { 485 public final MethodIdItem method; 486 public final AnnotationSetItem annotationSet; 487 488 public MethodAnnotation(MethodIdItem method, AnnotationSetItem annotationSet) { 489 this.method = method; 490 this.annotationSet = annotationSet; 491 } 492 493 public int compareTo(MethodAnnotation other) { 494 return method.compareTo(other.method); 495 } 496 } 497 498 public static class ParameterAnnotation implements Comparable<ParameterAnnotation> { 499 public final MethodIdItem method; 500 public final AnnotationSetRefList annotationSet; 501 502 public ParameterAnnotation(MethodIdItem method, AnnotationSetRefList annotationSet) { 503 this.method = method; 504 this.annotationSet = annotationSet; 505 } 506 507 public int compareTo(ParameterAnnotation other) { 508 return method.compareTo(other.method); 509 } 510 } 511} 512