/*
* [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.AnnotatedOutput;
import org.jf.dexlib.Util.ExceptionWithContext;
import org.jf.dexlib.Util.Input;
import java.util.Collections;
import java.util.List;
public class AnnotationDirectoryItem extends Item<AnnotationDirectoryItem> {
private AnnotationSetItem classAnnotations;
private FieldIdItem[] fieldAnnotationFields;
private AnnotationSetItem[] fieldAnnotations;
private MethodIdItem[] methodAnnotationMethods;
private AnnotationSetItem[] methodAnnotations;
private MethodIdItem[] parameterAnnotationMethods;
private AnnotationSetRefList[] parameterAnnotations;
/**
* typically each AnnotationDirectoryItem will have a distinct parent. The only case that isn't true is when
* the AnnotationDirectoryItem *only* contains class annotations, with no other type of annotation. In that
* case, the same AnnotationDirectoryItem could be referenced from multiple classes.
* This isn't a problem though, because this field is only used in compareTo to determine the sort order,
* which handles it as a special case
*/
private ClassDefItem parent = null;
/**
* Creates a new uninitialized <code>AnnotationDirectoryItem</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
*/
protected AnnotationDirectoryItem(DexFile dexFile) {
super(dexFile);
}
/**
* Creates a new <code>AnnotationDirectoryItem</code> with the given values
* @param dexFile The <code>DexFile</code> that this item belongs to
* @param classAnnotations The annotations associated with the overall class
* @param fieldAnnotationFields An array of <code>FieldIdItem</code> objects that the annotations in
* <code>fieldAnnotations</code> are associated with
* @param fieldAnnotations An array of <code>AnnotationSetItem</code> objects that contain the annotations for the
* fields in <code>fieldAnnotationFields</code>
* @param methodAnnotationMethods An array of <code>MethodIdItem</code> objects that the annotations in
* <code>methodAnnotations</code> are associated with
* @param methodAnnotations An array of <code>AnnotationSetItem</code> objects that contain the annotations for the
* methods in <code>methodAnnotationMethods</code>
* @param parameterAnnotationMethods An array of <code>MethodIdItem</code> objects that the annotations in
* <code>parameterAnnotations</code> are associated with
* @param parameterAnnotations An array of <code>AnnotationSetRefList</code> objects that contain the parameter
* annotations for the methods in <code>parameterAnnotationMethods</code>
*/
private AnnotationDirectoryItem(DexFile dexFile, AnnotationSetItem classAnnotations,
FieldIdItem[] fieldAnnotationFields, AnnotationSetItem[] fieldAnnotations,
MethodIdItem[] methodAnnotationMethods, AnnotationSetItem[] methodAnnotations,
MethodIdItem[] parameterAnnotationMethods,
AnnotationSetRefList[] parameterAnnotations) {
super(dexFile);
this.classAnnotations = classAnnotations;
this.fieldAnnotationFields = fieldAnnotationFields;
this.fieldAnnotations = fieldAnnotations;
this.methodAnnotationMethods = methodAnnotationMethods;
this.methodAnnotations = methodAnnotations;
this.parameterAnnotationMethods = parameterAnnotationMethods;
this.parameterAnnotations = parameterAnnotations;
}
/**
* Returns an <code>AnnotationDirectoryItem</code> for the given values, and that has been interned into the given
* <code>DexFile</code>
* @param dexFile The <code>DexFile</code> that this item belongs to
* @param classAnnotations The annotations associated with the class
* @param fieldAnnotations A list of <code>FieldAnnotation</code> objects containing the field annotations
* @param methodAnnotations A list of <code>MethodAnnotation</code> objects containing the method annotations
* @param parameterAnnotations A list of <code>ParameterAnnotation</code> objects containin the parameter
* annotations
* @return an <code>AnnotationItem</code> for the given values, and that has been interned into the given
* <code>DexFile</code>
*/
public static AnnotationDirectoryItem internAnnotationDirectoryItem(DexFile dexFile,
AnnotationSetItem classAnnotations,
List<FieldAnnotation> fieldAnnotations,
List<MethodAnnotation> methodAnnotations,
List<ParameterAnnotation> parameterAnnotations) {
FieldIdItem[] fieldAnnotationFields = null;
AnnotationSetItem[] fieldAnnotationsArray = null;
MethodIdItem[] methodAnnotationMethods = null;
AnnotationSetItem[] methodAnnotationsArray = null;
MethodIdItem[] parameterAnnotationMethods = null;
AnnotationSetRefList[] parameterAnnotationsArray = null;
if (fieldAnnotations != null && fieldAnnotations.size() > 0) {
fieldAnnotationFields = new FieldIdItem[fieldAnnotations.size()];
fieldAnnotationsArray = new AnnotationSetItem[fieldAnnotations.size()];
Collections.sort(fieldAnnotations);
int index = 0;
for (FieldAnnotation fieldAnnotation: fieldAnnotations) {
fieldAnnotationFields[index] = fieldAnnotation.field;
fieldAnnotationsArray[index++] = fieldAnnotation.annotationSet;
}
}
if (methodAnnotations != null && methodAnnotations.size() > 0) {
methodAnnotationMethods = new MethodIdItem[methodAnnotations.size()];
methodAnnotationsArray = new AnnotationSetItem[methodAnnotations.size()];
Collections.sort(methodAnnotations);
int index = 0;
for (MethodAnnotation methodAnnotation: methodAnnotations) {
methodAnnotationMethods[index] = methodAnnotation.method;
methodAnnotationsArray[index++] = methodAnnotation.annotationSet;
}
}
if (parameterAnnotations != null && parameterAnnotations.size() > 0) {
parameterAnnotationMethods = new MethodIdItem[parameterAnnotations.size()];
parameterAnnotationsArray = new AnnotationSetRefList[parameterAnnotations.size()];
Collections.sort(parameterAnnotations);
int index = 0;
for (ParameterAnnotation parameterAnnotation: parameterAnnotations) {
parameterAnnotationMethods[index] = parameterAnnotation.method;
parameterAnnotationsArray[index++] = parameterAnnotation.annotationSet;
}
}
AnnotationDirectoryItem annotationDirectoryItem = new AnnotationDirectoryItem(dexFile, classAnnotations,
fieldAnnotationFields, fieldAnnotationsArray, methodAnnotationMethods, methodAnnotationsArray,
parameterAnnotationMethods, parameterAnnotationsArray);
return dexFile.AnnotationDirectoriesSection.intern(annotationDirectoryItem);
}
/** {@inheritDoc} */
protected void readItem(Input in, ReadContext readContext) {
classAnnotations = (AnnotationSetItem)readContext.getOptionalOffsettedItemByOffset(
ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
fieldAnnotationFields = new FieldIdItem[in.readInt()];
fieldAnnotations = new AnnotationSetItem[fieldAnnotationFields.length];
methodAnnotationMethods = new MethodIdItem[in.readInt()];
methodAnnotations = new AnnotationSetItem[methodAnnotationMethods.length];
parameterAnnotationMethods = new MethodIdItem[in.readInt()];
parameterAnnotations = new AnnotationSetRefList[parameterAnnotationMethods.length];
for (int i=0; i<fieldAnnotations.length; i++) {
try {
fieldAnnotationFields[i] = dexFile.FieldIdsSection.getItemByIndex(in.readInt());
fieldAnnotations[i] = (AnnotationSetItem)readContext.getOffsettedItemByOffset(
ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
} catch (Exception ex) {
throw ExceptionWithContext.withContext(ex,
"Error occured while reading FieldAnnotation at index " + i);
}
}
for (int i=0; i<methodAnnotations.length; i++) {
try {
methodAnnotationMethods[i] = dexFile.MethodIdsSection.getItemByIndex(in.readInt());
methodAnnotations[i] = (AnnotationSetItem)readContext.getOffsettedItemByOffset(
ItemType.TYPE_ANNOTATION_SET_ITEM, in.readInt());
} catch (Exception ex) {
throw ExceptionWithContext.withContext(ex,
"Error occured while reading MethodAnnotation at index " + i);
}
}
for (int i=0; i<parameterAnnotations.length; i++) {
try {
parameterAnnotationMethods[i] = dexFile.MethodIdsSection.getItemByIndex(in.readInt());
parameterAnnotations[i] = (AnnotationSetRefList)readContext.getOffsettedItemByOffset(
ItemType.TYPE_ANNOTATION_SET_REF_LIST, in.readInt());
} catch (Exception ex) {
throw ExceptionWithContext.withContext(ex,
"Error occured while reading ParameterAnnotation at index " + i);
}
}
}
/** {@inheritDoc} */
protected int placeItem(int offset) {
return offset + 16 + (
(fieldAnnotations==null?0:fieldAnnotations.length) +
(methodAnnotations==null?0:methodAnnotations.length) +
(parameterAnnotations==null?0:parameterAnnotations.length)) * 8;
}
/** {@inheritDoc} */
protected void writeItem(AnnotatedOutput out) {
if (out.annotates()) {
if (!isInternable() && parent != null) {
out.annotate(0, parent.getClassType().getTypeDescriptor());
}
if (classAnnotations != null) {
out.annotate(4, "class_annotations_off: 0x" + Integer.toHexString(classAnnotations.getOffset()));
} else {
out.annotate(4, "class_annotations_off:");
}
int length = fieldAnnotations==null?0:fieldAnnotations.length;
out.annotate(4, "annotated_fields_size: 0x" + Integer.toHexString(length) + " (" +
length + ")");
length = methodAnnotations==null?0:methodAnnotations.length;
out.annotate(4, "annotated_methods_size: 0x" + Integer.toHexString(length) + " (" +
length + ")");
length = parameterAnnotations==null?0:parameterAnnotations.length;
out.annotate(4, "annotated_parameters_size: 0x" + Integer.toHexString(length) + " (" +
length + ")");
int index;
if (fieldAnnotations != null) {
index = 0;
for (int i=0; i<fieldAnnotations.length; i++) {
out.annotate(0, "[" + index++ + "] field_annotation");
out.indent();
out.annotate(4, "field: " + fieldAnnotationFields[i].getFieldName().getStringValue() + ":" +
fieldAnnotationFields[i].getFieldType().getTypeDescriptor());
out.annotate(4, "annotations_off: 0x" + Integer.toHexString(fieldAnnotations[i].getOffset()));
out.deindent();
}
}
if (methodAnnotations != null) {
index = 0;
for (int i=0; i<methodAnnotations.length; i++) {
out.annotate(0, "[" + index++ + "] method_annotation");
out.indent();
out.annotate(4, "method: " + methodAnnotationMethods[i].getMethodString());
out.annotate(4, "annotations_off: 0x" + Integer.toHexString(methodAnnotations[i].getOffset()));
out.deindent();
}
}
if (parameterAnnotations != null) {
index = 0;
for (int i=0; i<parameterAnnotations.length; i++) {
out.annotate(0, "[" + index++ + "] parameter_annotation");
out.indent();
out.annotate(4, "method: " + parameterAnnotationMethods[i].getMethodString());
out.annotate(4, "annotations_off: 0x" + Integer.toHexString(parameterAnnotations[i].getOffset()));
}
}
}
out.writeInt(classAnnotations==null?0:classAnnotations.getOffset());
out.writeInt(fieldAnnotations==null?0:fieldAnnotations.length);
out.writeInt(methodAnnotations==null?0:methodAnnotations.length);
out.writeInt(parameterAnnotations==null?0:parameterAnnotations.length);
if (fieldAnnotations != null) {
for (int i=0; i<fieldAnnotations.length; i++) {
out.writeInt(fieldAnnotationFields[i].getIndex());
out.writeInt(fieldAnnotations[i].getOffset());
}
}
if (methodAnnotations != null) {
for (int i=0; i<methodAnnotations.length; i++) {
out.writeInt(methodAnnotationMethods[i].getIndex());
out.writeInt(methodAnnotations[i].getOffset());
}
}
if (parameterAnnotations != null) {
for (int i=0; i<parameterAnnotations.length; i++) {
out.writeInt(parameterAnnotationMethods[i].getIndex());
out.writeInt(parameterAnnotations[i].getOffset());
}
}
}
/** {@inheritDoc} */
public ItemType getItemType() {
return ItemType.TYPE_ANNOTATIONS_DIRECTORY_ITEM;
}
/** {@inheritDoc} */
public String getConciseIdentity() {
if (parent == null) {
return "annotation_directory_item @0x" + Integer.toHexString(getOffset());
}
return "annotation_directory_item @0x" + Integer.toHexString(getOffset()) + " (" + parent.getClassType() + ")";
}
/** {@inheritDoc} */
public int compareTo(AnnotationDirectoryItem o) {
if (!isInternable()) {
if (!o.isInternable()) {
return parent.compareTo(o.parent);
}
return -1;
}
if (!o.isInternable()) {
return 1;
}
return classAnnotations.compareTo(o.classAnnotations);
}
/**
* @return The annotations associated with the class
*/
public AnnotationSetItem getClassAnnotations() {
return classAnnotations;
}
/**
* Iterates over the field annotations, calling delegate.processFieldAnnotations for each
* @param delegate the delegate to call
*/
public void iterateFieldAnnotations(FieldAnnotationIteratorDelegate delegate) {
for (int i=0; i<fieldAnnotationFields.length; i++) {
try {
delegate.processFieldAnnotations(fieldAnnotationFields[i], fieldAnnotations[i]);
} catch (Exception ex) {
throw addExceptionContext(ExceptionWithContext.withContext(ex,
"Error occured while processing field annotations for field: " +
fieldAnnotationFields[i].getFieldString()));
}
}
}
public static interface FieldAnnotationIteratorDelegate {
void processFieldAnnotations(FieldIdItem field, AnnotationSetItem fieldAnnotations);
}
/**
* @return the number of field annotations in this <code>AnnotationDirectoryItem</code>
*/
public int getFieldAnnotationCount() {
return fieldAnnotationFields.length;
}
/**
* Iterates over the method annotations, calling delegate.processMethodAnnotations for each
* @param delegate the delegate to call
*/
public void iterateMethodAnnotations(MethodAnnotationIteratorDelegate delegate) {
for (int i=0; i<methodAnnotationMethods.length; i++) {
try {
delegate.processMethodAnnotations(methodAnnotationMethods[i], methodAnnotations[i]);
} catch (Exception ex) {
throw addExceptionContext(ExceptionWithContext.withContext(ex,
"Error occured while processing method annotations for method: " +
methodAnnotationMethods[i].getMethodString()));
}
}
}
public static interface MethodAnnotationIteratorDelegate {
void processMethodAnnotations(MethodIdItem method, AnnotationSetItem methodAnnotations);
}
/**
* @return the number of method annotations in this <code>AnnotationDirectoryItem</code>
*/
public int getMethodAnnotationCount() {
return methodAnnotationMethods.length;
}
/**
* Iterates over the parameter annotations, calling delegate.processParameterAnnotations for each
* @param delegate the delegate to call
*/
public void iterateParameterAnnotations(ParameterAnnotationIteratorDelegate delegate) {
for (int i=0; i<parameterAnnotationMethods.length; i++) {
try {
delegate.processParameterAnnotations(parameterAnnotationMethods[i], parameterAnnotations[i]);
} catch (Exception ex) {
throw addExceptionContext(ExceptionWithContext.withContext(ex,
"Error occured while processing parameter annotations for method: " +
parameterAnnotationMethods[i].getMethodString()));
}
}
}
public static interface ParameterAnnotationIteratorDelegate {
void processParameterAnnotations(MethodIdItem method, AnnotationSetRefList parameterAnnotations);
}
/**
* @return the number of parameter annotations in this <code>AnnotationDirectoryItem</code>
*/
public int getParameterAnnotationCount() {
return parameterAnnotationMethods.length;
}
/**
* @return true if this <code>AnnotationDirectoryItem</code> is internable. It is only internable if it has
* only class annotations, but no field, method or parameter annotations
*/
private boolean isInternable() {
return classAnnotations != null &&
(fieldAnnotations == null || fieldAnnotations.length == 0) &&
(methodAnnotations == null || methodAnnotations.length == 0) &&
(parameterAnnotations == null || parameterAnnotations.length == 0);
}
/**
* Sets the <code>ClassDefItem</code> that this <code>AnnotationDirectoryItem</code> is associated with.
* This is only applicable if this AnnotationDirectoryItem contains only class annotations, and no field, method
* or parameter annotations.
* @param classDefItem the <code>ClassDefItem</code> that this <code>AnnotationDirectoryItem</code> is associated
* with
*/
protected void setParent(ClassDefItem classDefItem) {
this.parent = classDefItem;
}
@Override
public int hashCode() {
//an instance is only internable if it has only class annotations, but
//no other type of annotation
if (!isInternable()) {
return super.hashCode();
}
return classAnnotations.hashCode();
}
@Override
public boolean equals(Object o) {
if (this==o) {
return true;
}
if (o==null || !this.getClass().equals(o.getClass())) {
return false;
}
AnnotationDirectoryItem other = (AnnotationDirectoryItem)o;
return (this.compareTo(other) == 0);
}
public static class FieldAnnotation implements Comparable<FieldAnnotation> {
public final FieldIdItem field;
public final AnnotationSetItem annotationSet;
public FieldAnnotation(FieldIdItem field, AnnotationSetItem annotationSet) {
this.field = field;
this.annotationSet = annotationSet;
}
public int compareTo(FieldAnnotation other) {
return field.compareTo(other.field);
}
}
public static class MethodAnnotation implements Comparable<MethodAnnotation> {
public final MethodIdItem method;
public final AnnotationSetItem annotationSet;
public MethodAnnotation(MethodIdItem method, AnnotationSetItem annotationSet) {
this.method = method;
this.annotationSet = annotationSet;
}
public int compareTo(MethodAnnotation other) {
return method.compareTo(other.method);
}
}
public static class ParameterAnnotation implements Comparable<ParameterAnnotation> {
public final MethodIdItem method;
public final AnnotationSetRefList annotationSet;
public ParameterAnnotation(MethodIdItem method, AnnotationSetRefList annotationSet) {
this.method = method;
this.annotationSet = annotationSet;
}
public int compareTo(ParameterAnnotation other) {
return method.compareTo(other.method);
}
}
}