package net.bytebuddy.implementation.attribute;
import lombok.EqualsAndHashCode;
import net.bytebuddy.description.annotation.AnnotationDescription;
import net.bytebuddy.description.annotation.AnnotationList;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.description.type.TypeList;
import org.objectweb.asm.ClassVisitor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* An appender that writes attributes or annotations to a given ASM {@link org.objectweb.asm.ClassVisitor}.
*/
public interface TypeAttributeAppender {
/**
* Applies this type attribute appender.
*
* @param classVisitor The class visitor to which the annotations of this visitor should be written to.
* @param instrumentedType A description of the instrumented type that is target of the ongoing instrumentation.
* @param annotationValueFilter The annotation value filter to apply when writing annotations.
*/
void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter);
/**
* A type attribute appender that does not append any attributes.
*/
enum NoOp implements TypeAttributeAppender {
/**
* The singleton instance.
*/
INSTANCE;
@Override
public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) {
/* do nothing */
}
}
/**
* An attribute appender that writes all annotations that are found on a given target type to the
* instrumented type this type attribute appender is applied onto. The visibility for the annotation
* will be inferred from the annotations' {@link java.lang.annotation.RetentionPolicy}.
*/
enum ForInstrumentedType implements TypeAttributeAppender {
/**
* The singleton instance.
*/
INSTANCE;
@Override
public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) {
AnnotationAppender annotationAppender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnType(classVisitor));
annotationAppender = AnnotationAppender.ForTypeAnnotations.ofTypeVariable(annotationAppender,
annotationValueFilter,
AnnotationAppender.ForTypeAnnotations.VARIABLE_ON_TYPE,
instrumentedType.getTypeVariables());
TypeDescription.Generic superClass = instrumentedType.getSuperClass();
if (superClass != null) {
annotationAppender = superClass.accept(AnnotationAppender.ForTypeAnnotations.ofSuperClass(annotationAppender, annotationValueFilter));
}
int interfaceIndex = 0;
for (TypeDescription.Generic interfaceType : instrumentedType.getInterfaces()) {
annotationAppender = interfaceType.accept(AnnotationAppender.ForTypeAnnotations.ofInterfaceType(annotationAppender,
annotationValueFilter,
interfaceIndex++));
}
for (AnnotationDescription annotation : instrumentedType.getDeclaredAnnotations()) {
annotationAppender = annotationAppender.append(annotation, annotationValueFilter);
}
}
/**
* A type attribute appender that writes all annotations of the instrumented but excludes annotations up to
* a given index.
*/
@EqualsAndHashCode
public static class Differentiating implements TypeAttributeAppender {
/**
* The index of the first annotations that should be directly written onto the type.
*/
private final int annotationIndex;
/**
* The index of the first type variable for which type annotations should be directly written onto the type.
*/
private final int typeVariableIndex;
/**
* The index of the first interface type for which type annotations should be directly written onto the type.
*/
private final int interfaceTypeIndex;
/**
* Creates a new differentiating type attribute appender.
*
* @param typeDescription The type for which to resolve all exclusion indices.
*/
public Differentiating(TypeDescription typeDescription) {
this(typeDescription.getDeclaredAnnotations().size(), typeDescription.getTypeVariables().size(), typeDescription.getInterfaces().size());
}
/**
* Creates a new differentiating type attribute appender.
*
* @param annotationIndex The index of the first annotations that should be directly written onto the type.
* @param typeVariableIndex The index of the first interface type for which type annotations should be directly written onto the type.
* @param interfaceTypeIndex The index of the first interface type for which type annotations should be directly written onto the type.
*/
protected Differentiating(int annotationIndex, int typeVariableIndex, int interfaceTypeIndex) {
this.annotationIndex = annotationIndex;
this.typeVariableIndex = typeVariableIndex;
this.interfaceTypeIndex = interfaceTypeIndex;
}
@Override
public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) {
AnnotationAppender annotationAppender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnType(classVisitor));
AnnotationAppender.ForTypeAnnotations.ofTypeVariable(annotationAppender,
annotationValueFilter,
AnnotationAppender.ForTypeAnnotations.VARIABLE_ON_TYPE,
typeVariableIndex,
instrumentedType.getTypeVariables());
TypeList.Generic interfaceTypes = instrumentedType.getInterfaces();
int interfaceTypeIndex = this.interfaceTypeIndex;
for (TypeDescription.Generic interfaceType : interfaceTypes.subList(this.interfaceTypeIndex, interfaceTypes.size())) {
annotationAppender = interfaceType.accept(AnnotationAppender.ForTypeAnnotations.ofInterfaceType(annotationAppender,
annotationValueFilter,
interfaceTypeIndex++));
}
AnnotationList declaredAnnotations = instrumentedType.getDeclaredAnnotations();
for (AnnotationDescription annotationDescription : declaredAnnotations.subList(annotationIndex, declaredAnnotations.size())) {
annotationAppender = annotationAppender.append(annotationDescription, annotationValueFilter);
}
}
}
}
/**
* An attribute appender that appends a single annotation to a given type. The visibility for the annotation
* will be inferred from the annotation's {@link java.lang.annotation.RetentionPolicy}.
*/
@EqualsAndHashCode
class Explicit implements TypeAttributeAppender {
/**
* The annotations to write to the given type.
*/
private final List<? extends AnnotationDescription> annotations;
/**
* Creates a new annotation attribute appender for explicit annotation values.
*
* @param annotations The annotations to write to the given type.
*/
public Explicit(List<? extends AnnotationDescription> annotations) {
this.annotations = annotations;
}
@Override
public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) {
AnnotationAppender appender = new AnnotationAppender.Default(new AnnotationAppender.Target.OnType(classVisitor));
for (AnnotationDescription annotation : annotations) {
appender = appender.append(annotation, annotationValueFilter);
}
}
}
/**
* A compound type attribute appender that concatenates a number of other attribute appenders.
*/
@EqualsAndHashCode
class Compound implements TypeAttributeAppender {
/**
* The type attribute appenders this compound appender represents in their application order.
*/
private final List<TypeAttributeAppender> typeAttributeAppenders;
/**
* Creates a new compound attribute appender.
*
* @param typeAttributeAppender The type attribute appenders to concatenate in the order of their application.
*/
public Compound(TypeAttributeAppender... typeAttributeAppender) {
this(Arrays.asList(typeAttributeAppender));
}
/**
* Creates a new compound attribute appender.
*
* @param typeAttributeAppenders The type attribute appenders to concatenate in the order of their application.
*/
public Compound(List<? extends TypeAttributeAppender> typeAttributeAppenders) {
this.typeAttributeAppenders = new ArrayList<TypeAttributeAppender>();
for (TypeAttributeAppender typeAttributeAppender : typeAttributeAppenders) {
if (typeAttributeAppender instanceof Compound) {
this.typeAttributeAppenders.addAll(((Compound) typeAttributeAppender).typeAttributeAppenders);
} else if (!(typeAttributeAppender instanceof NoOp)) {
this.typeAttributeAppenders.add(typeAttributeAppender);
}
}
}
@Override
public void apply(ClassVisitor classVisitor, TypeDescription instrumentedType, AnnotationValueFilter annotationValueFilter) {
for (TypeAttributeAppender typeAttributeAppender : typeAttributeAppenders) {
typeAttributeAppender.apply(classVisitor, instrumentedType, annotationValueFilter);
}
}
}
}