package org.jf.dexlib2.dexbacked.util;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.jf.dexlib2.dexbacked.DexBackedAnnotation;
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import javax.annotation.Nonnull;
import java.util.List;
import java.util.Set;
public abstract class AnnotationsDirectory {
public static final AnnotationsDirectory EMPTY = new AnnotationsDirectory() {
@Override public int getFieldAnnotationCount() { return 0; }
@Nonnull @Override public Set<? extends DexBackedAnnotation> getClassAnnotations() { return ImmutableSet.of(); }
@Nonnull @Override public AnnotationIterator getFieldAnnotationIterator() { return AnnotationIterator.EMPTY; }
@Nonnull @Override public AnnotationIterator getMethodAnnotationIterator() { return AnnotationIterator.EMPTY; }
@Nonnull @Override public AnnotationIterator getParameterAnnotationIterator() {return AnnotationIterator.EMPTY;}
};
public abstract int getFieldAnnotationCount();
@Nonnull public abstract Set<? extends DexBackedAnnotation> getClassAnnotations();
@Nonnull public abstract AnnotationIterator getFieldAnnotationIterator();
@Nonnull public abstract AnnotationIterator getMethodAnnotationIterator();
@Nonnull public abstract AnnotationIterator getParameterAnnotationIterator();
@Nonnull
public static AnnotationsDirectory newOrEmpty(@Nonnull DexBackedDexFile dexFile,
int directoryAnnotationsOffset) {
if (directoryAnnotationsOffset == 0) {
return EMPTY;
}
return new AnnotationsDirectoryImpl(dexFile, directoryAnnotationsOffset);
}
public interface AnnotationIterator {
public static final AnnotationIterator EMPTY = new AnnotationIterator() {
@Override public int seekTo(int key) { return 0; }
@Override public void reset() {}
};
public int seekTo(int key);
public void reset();
}
@Nonnull
public static Set<? extends DexBackedAnnotation> getAnnotations(@Nonnull final DexBackedDexFile dexFile,
final int annotationSetOffset) {
if (annotationSetOffset != 0) {
final int size = dexFile.getDataBuffer().readSmallUint(annotationSetOffset);
return new FixedSizeSet<DexBackedAnnotation>() {
@Nonnull
@Override
public DexBackedAnnotation readItem(int index) {
int annotationOffset = dexFile.getDataBuffer().readSmallUint(annotationSetOffset + 4 + (4*index));
return new DexBackedAnnotation(dexFile, annotationOffset);
}
@Override public int size() { return size; }
};
}
return ImmutableSet.of();
}
@Nonnull
public static List<Set<? extends DexBackedAnnotation>> getParameterAnnotations(
@Nonnull final DexBackedDexFile dexFile, final int annotationSetListOffset) {
if (annotationSetListOffset > 0) {
final int size = dexFile.getDataBuffer().readSmallUint(annotationSetListOffset);
return new FixedSizeList<Set<? extends DexBackedAnnotation>>() {
@Nonnull
@Override
public Set<? extends DexBackedAnnotation> readItem(int index) {
int annotationSetOffset = dexFile.getDataBuffer().readSmallUint(
annotationSetListOffset + 4 + index * 4);
return getAnnotations(dexFile, annotationSetOffset);
}
@Override public int size() { return size; }
};
}
return ImmutableList.of();
}
private static class AnnotationsDirectoryImpl extends AnnotationsDirectory {
@Nonnull public final DexBackedDexFile dexFile;
private final int directoryOffset;
private static final int FIELD_COUNT_OFFSET = 4;
private static final int METHOD_COUNT_OFFSET = 8;
private static final int PARAMETER_COUNT_OFFSET = 12;
private static final int ANNOTATIONS_START_OFFSET = 16;
private static final int FIELD_ANNOTATION_SIZE = 8;
private static final int METHOD_ANNOTATION_SIZE = 8;
public AnnotationsDirectoryImpl(@Nonnull DexBackedDexFile dexFile,
int directoryOffset) {
this.dexFile = dexFile;
this.directoryOffset = directoryOffset;
}
public int getFieldAnnotationCount() {
return dexFile.getDataBuffer().readSmallUint(directoryOffset + FIELD_COUNT_OFFSET);
}
public int getMethodAnnotationCount() {
return dexFile.getDataBuffer().readSmallUint(directoryOffset + METHOD_COUNT_OFFSET);
}
public int getParameterAnnotationCount() {
return dexFile.getDataBuffer().readSmallUint(directoryOffset + PARAMETER_COUNT_OFFSET);
}
@Nonnull
public Set<? extends DexBackedAnnotation> getClassAnnotations() {
return getAnnotations(dexFile, dexFile.getDataBuffer().readSmallUint(directoryOffset));
}
@Nonnull
public AnnotationIterator getFieldAnnotationIterator() {
int fieldAnnotationCount = getFieldAnnotationCount();
if (fieldAnnotationCount == 0) {
return AnnotationIterator.EMPTY;
}
return new AnnotationIteratorImpl(directoryOffset + ANNOTATIONS_START_OFFSET, fieldAnnotationCount);
}
@Nonnull
public AnnotationIterator getMethodAnnotationIterator() {
int methodCount = getMethodAnnotationCount();
if (methodCount == 0) {
return AnnotationIterator.EMPTY;
}
int fieldCount = getFieldAnnotationCount();
int methodAnnotationsOffset = directoryOffset + ANNOTATIONS_START_OFFSET +
fieldCount * FIELD_ANNOTATION_SIZE;
return new AnnotationIteratorImpl(methodAnnotationsOffset, methodCount);
}
@Nonnull
public AnnotationIterator getParameterAnnotationIterator() {
int parameterAnnotationCount = getParameterAnnotationCount();
if (parameterAnnotationCount == 0) {
return AnnotationIterator.EMPTY;
}
int fieldCount = getFieldAnnotationCount();
int methodCount = getMethodAnnotationCount();
int parameterAnnotationsOffset = directoryOffset + ANNOTATIONS_START_OFFSET +
fieldCount * FIELD_ANNOTATION_SIZE +
methodCount * METHOD_ANNOTATION_SIZE;
return new AnnotationIteratorImpl(parameterAnnotationsOffset, parameterAnnotationCount);
}
private class AnnotationIteratorImpl implements AnnotationIterator {
private final int startOffset;
private final int size;
private int currentIndex;
private int currentItemIndex;
public AnnotationIteratorImpl(int startOffset, int size) {
this.startOffset = startOffset;
this.size = size;
this.currentItemIndex = dexFile.getDataBuffer().readSmallUint(startOffset);
this.currentIndex = 0;
}
public int seekTo(int itemIndex) {
while (currentItemIndex < itemIndex && (currentIndex+1) < size) {
currentIndex++;
currentItemIndex = dexFile.getDataBuffer().readSmallUint(startOffset + (currentIndex*8));
}
if (currentItemIndex == itemIndex) {
return dexFile.getDataBuffer().readSmallUint(startOffset + (currentIndex*8)+4);
}
return 0;
}
public void reset() {
this.currentItemIndex = dexFile.getDataBuffer().readSmallUint(startOffset);
this.currentIndex = 0;
}
}
}
}