org.testifyproject.bytebuddy.implementation.bind.annotation.FieldValue Maven / Gradle / Ivy
package org.testifyproject.bytebuddy.implementation.bind.annotation;
import org.testifyproject.bytebuddy.description.annotation.AnnotationDescription;
import org.testifyproject.bytebuddy.description.field.FieldDescription;
import org.testifyproject.bytebuddy.description.method.MethodDescription;
import org.testifyproject.bytebuddy.description.method.MethodList;
import org.testifyproject.bytebuddy.description.method.ParameterDescription;
import org.testifyproject.bytebuddy.description.type.TypeDescription;
import org.testifyproject.bytebuddy.implementation.Implementation;
import org.testifyproject.bytebuddy.implementation.bind.MethodDelegationBinder;
import org.testifyproject.bytebuddy.implementation.bytecode.StackManipulation;
import org.testifyproject.bytebuddy.implementation.bytecode.assign.Assigner;
import org.testifyproject.bytebuddy.implementation.bytecode.member.FieldAccess;
import org.testifyproject.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
import java.lang.annotation.*;
import static org.testifyproject.bytebuddy.matcher.ElementMatchers.named;
/**
*
* Assigns the value of a field of the instrumented type to the annotated parameter. For a binding to be valid,
* the instrumented type must be able to access a field of the given name. Also, the parameter's type must be
* assignable to the given field. For attempting a type casting, the {@link RuntimeType} annotation can be
* applied to the parameter.
*
*
* Setting {@link FieldValue#value()} is optional. If the value is not set, the field value attempts to bind a setter's
* or getter's field if the intercepted method is an accessor method. Otherwise, the binding renders the target method
* to be an illegal candidate for binding.
*
*
* @see org.testifyproject.bytebuddy.implementation.MethodDelegation
* @see org.testifyproject.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder
* @see org.testifyproject.bytebuddy.implementation.bind.annotation.RuntimeType
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface FieldValue {
/**
* The name of the field to be accessed.
*
* @return The name of the field.
*/
String value() default TargetMethodAnnotationDrivenBinder.ParameterBinder.ForFieldBinding.BEAN_PROPERTY;
/**
* Defines the type on which the field is declared. If this value is not set, the most specific type's field is read,
* if two fields with the same name exist in the same type hierarchy.
*
* @return The type that declares the accessed field.
*/
Class> declaringType() default void.class;
/**
* Binds a {@link FieldValue} annotation.
*/
enum Binder implements TargetMethodAnnotationDrivenBinder.ParameterBinder {
/**
* The singleton instance.
*/
INSTANCE(new Delegate());
/**
* The annotation method that for the defining type.
*/
private static final MethodDescription.InDefinedShape DECLARING_TYPE;
/**
* The annotation method for the field's name.
*/
private static final MethodDescription.InDefinedShape FIELD_NAME;
/*
* Initializes the methods of the annotation that is read by this binder.
*/
static {
MethodList methodList = new TypeDescription.ForLoadedType(FieldValue.class).getDeclaredMethods();
DECLARING_TYPE = methodList.filter(named("declaringType")).getOnly();
FIELD_NAME = methodList.filter(named("value")).getOnly();
}
/**
* A delegate parameter binder responsible for binding the parameter.
*/
private final TargetMethodAnnotationDrivenBinder.ParameterBinder delegate;
/**
* Creates a new binder for a {@link FieldValue}.
*
* @param delegate A delegate parameter binder responsible for binding the parameter.
*/
Binder(TargetMethodAnnotationDrivenBinder.ParameterBinder delegate) {
this.delegate = delegate;
}
@Override
public Class getHandledType() {
return delegate.getHandledType();
}
@Override
public MethodDelegationBinder.ParameterBinding> bind(AnnotationDescription.Loadable annotation,
MethodDescription source,
ParameterDescription target,
Implementation.Target implementationTarget,
Assigner assigner,
Assigner.Typing typing) {
return delegate.bind(annotation, source, target, implementationTarget, assigner, typing);
}
/**
* A delegate implementation for the {@link FieldValue.Binder}.
*/
protected static class Delegate extends ForFieldBinding {
@Override
public Class getHandledType() {
return FieldValue.class;
}
@Override
protected MethodDelegationBinder.ParameterBinding> bind(FieldDescription fieldDescription,
AnnotationDescription.Loadable annotation,
MethodDescription source,
ParameterDescription target,
Implementation.Target implementationTarget,
Assigner assigner) {
StackManipulation stackManipulation = new StackManipulation.Compound(
fieldDescription.isStatic()
? StackManipulation.Trivial.INSTANCE
: MethodVariableAccess.loadThis(),
FieldAccess.forField(fieldDescription).read(),
assigner.assign(fieldDescription.getType(), target.getType(), RuntimeType.Verifier.check(target))
);
return stackManipulation.isValid()
? new MethodDelegationBinder.ParameterBinding.Anonymous(stackManipulation)
: MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE;
}
@Override
protected String fieldName(AnnotationDescription.Loadable annotation) {
return annotation.getValue(FIELD_NAME).resolve(String.class);
}
@Override
protected TypeDescription declaringType(AnnotationDescription.Loadable annotation) {
return annotation.getValue(DECLARING_TYPE).resolve(TypeDescription.class);
}
}
}
}