// Generated by delombok at Sun Feb 26 12:31:38 KST 2017
package scouter.bytebuddy.implementation.bind.annotation;
import scouter.bytebuddy.ByteBuddy;
import scouter.bytebuddy.ClassFileVersion;
import scouter.bytebuddy.description.annotation.AnnotationDescription;
import scouter.bytebuddy.description.field.FieldDescription;
import scouter.bytebuddy.description.field.FieldList;
import scouter.bytebuddy.description.method.MethodDescription;
import scouter.bytebuddy.description.method.MethodList;
import scouter.bytebuddy.description.method.ParameterDescription;
import scouter.bytebuddy.description.modifier.Visibility;
import scouter.bytebuddy.description.type.TypeDescription;
import scouter.bytebuddy.description.type.TypeList;
import scouter.bytebuddy.dynamic.DynamicType;
import scouter.bytebuddy.dynamic.scaffold.InstrumentedType;
import scouter.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import scouter.bytebuddy.implementation.Implementation;
import scouter.bytebuddy.implementation.MethodAccessorFactory;
import scouter.bytebuddy.implementation.auxiliary.AuxiliaryType;
import scouter.bytebuddy.implementation.bind.MethodDelegationBinder;
import scouter.bytebuddy.implementation.bytecode.ByteCodeAppender;
import scouter.bytebuddy.implementation.bytecode.Duplication;
import scouter.bytebuddy.implementation.bytecode.StackManipulation;
import scouter.bytebuddy.implementation.bytecode.TypeCreation;
import scouter.bytebuddy.implementation.bytecode.assign.Assigner;
import scouter.bytebuddy.implementation.bytecode.member.FieldAccess;
import scouter.bytebuddy.implementation.bytecode.member.MethodInvocation;
import scouter.bytebuddy.implementation.bytecode.member.MethodReturn;
import scouter.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
import scouter.bytebuddy.matcher.ElementMatchers;
import scouter.bytebuddy.jar.asm.MethodVisitor;
import scouter.bytebuddy.implementation.MethodDelegation;
import java.io.Serializable;
import java.lang.annotation.*;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* A target method parameter that is annotated with this annotation allows to forward an intercepted method
* invocation to another instance. The instance to which a method call is forwarded must be of the most specific
* type that declares the intercepted method on the intercepted type.
* <p> </p>
* Unfortunately, before Java 8, the Java Class Library does not define any interface type which takes a single
* {@link java.lang.Object} type and returns another {@link java.lang.Object} type. For this reason, a
* {@link Pipe.Binder} needs to be installed explicitly
* and registered on a {@link MethodDelegation}. The installed type is allowed to be an
* interface without any super types that declares a single method which maps an {@link java.lang.Object} type to
* a another {@link java.lang.Object} type as a result value. It is however not prohibited to use generics in the
* process. The following example demonstrates how the {@code @Pipe} annotation can be installed on a user type.
* As a preparation, one needs to define a type for which the {@code @Pipe} implements the forwarding behavior:
* <pre>
* interface Forwarder<T, S> {
* T forwardTo(S s);
* }
* </pre>
* Based on this type, one can now implement an interceptor:
* <pre>
* class Interceptor {
* private final Foo foo;
*
* public Interceptor(Foo foo) {
* this.foo = foo;
* }
*
* public String intercept(@Pipe Forwarder<String, Foo> forwarder) {
* return forwarder.forwardTo(foo);
* }
* }
* </pre>
* Using both of these types, one can now install the
* {@link Pipe.Binder} and register it on a
* {@link MethodDelegation}:
* <pre>
* MethodDelegation
* .to(new Interceptor(new Foo()))
* .appendParameterBinder(Pipe.Binder.install(ForwardingType.class))
* </pre>
*
* @see MethodDelegation
* @see TargetMethodAnnotationDrivenBinder
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
public @interface Pipe {
/**
* Determines if the generated proxy should be {@link java.io.Serializable}.
*
* @return {@code true} if the generated proxy should be {@link java.io.Serializable}.
*/
boolean serializableProxy() default false;
/**
* A {@link TargetMethodAnnotationDrivenBinder.ParameterBinder}
* for binding the {@link Pipe} annotation.
*/
class Binder implements TargetMethodAnnotationDrivenBinder.ParameterBinder<Pipe> {
/**
* The method which implements the behavior of forwarding a method invocation. This method needs to define
* a single non-static method with an {@link java.lang.Object} to {@link java.lang.Object} mapping.
*/
private final MethodDescription forwardingMethod;
/**
* Creates a new binder. This constructor is not doing any validation of the forwarding method and its
* declaring type. Such validation is normally performed by the
* {@link Pipe.Binder#install(Class)}
* method.
*
* @param forwardingMethod The method which implements the behavior of forwarding a method invocation. This
* method needs to define a single non-static method with an {@link java.lang.Object}
* to {@link java.lang.Object} mapping.
*/
protected Binder(MethodDescription forwardingMethod) {
this.forwardingMethod = forwardingMethod;
}
/**
* Installs a given type for use on a {@link Pipe}
* annotation. The given type must be an interface without any super interfaces and a single method which
* maps an {@link java.lang.Object} type to another {@link java.lang.Object} type. The use of generics is
* permitted.
*
* @param type The type to install.
* @return A binder for the {@link Pipe}
* annotation.
*/
public static TargetMethodAnnotationDrivenBinder.ParameterBinder<Pipe> install(Class<?> type) {
return install(new TypeDescription.ForLoadedType(type));
}
/**
* Installs a given type for use on a {@link Pipe}
* annotation. The given type must be an interface without any super interfaces and a single method which
* maps an {@link java.lang.Object} type to another {@link java.lang.Object} type. The use of generics is
* permitted.
*
* @param typeDescription The type to install.
* @return A binder for the {@link Pipe}
* annotation.
*/
public static TargetMethodAnnotationDrivenBinder.ParameterBinder<Pipe> install(TypeDescription typeDescription) {
return new Binder(onlyMethod(typeDescription));
}
/**
* Locates the only method of a type that is compatible to being overridden for invoking the proxy.
*
* @param typeDescription The type that is being installed.
* @return Its only method after validation.
*/
private static MethodDescription onlyMethod(TypeDescription typeDescription) {
if (!typeDescription.isInterface()) {
throw new IllegalArgumentException(typeDescription + " is not an interface");
} else if (!typeDescription.getInterfaces().isEmpty()) {
throw new IllegalArgumentException(typeDescription + " must not extend other interfaces");
} else if (!typeDescription.isPublic()) {
throw new IllegalArgumentException(typeDescription + " is mot public");
}
MethodList<?> methodCandidates = typeDescription.getDeclaredMethods().filter(ElementMatchers.isAbstract());
if (methodCandidates.size() != 1) {
throw new IllegalArgumentException(typeDescription + " must declare exactly one abstract method");
}
MethodDescription methodDescription = methodCandidates.getOnly();
if (!methodDescription.getReturnType().asErasure().represents(Object.class)) {
throw new IllegalArgumentException(methodDescription + " does not return an Object-type");
} else if (methodDescription.getParameters().size() != 1 || !methodDescription.getParameters().getOnly().getType().asErasure().represents(Object.class)) {
throw new IllegalArgumentException(methodDescription + " does not take a single Object-typed argument");
}
return methodDescription;
}
@Override
public Class<Pipe> getHandledType() {
return Pipe.class;
}
@Override
public MethodDelegationBinder.ParameterBinding<?> bind(AnnotationDescription.Loadable<Pipe> annotation, MethodDescription source, ParameterDescription target, Implementation.Target implementationTarget, Assigner assigner, Assigner.Typing typing) {
if (!target.getType().asErasure().equals(forwardingMethod.getDeclaringType())) {
throw new IllegalStateException("Illegal use of @Pipe for " + target + " which was installed for " + forwardingMethod.getDeclaringType());
} else if (source.isStatic()) {
return MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE;
}
return new MethodDelegationBinder.ParameterBinding.Anonymous(new Redirection(forwardingMethod.getDeclaringType().asErasure(), source, assigner, annotation.loadSilent().serializableProxy()));
}
/**
* An auxiliary type for performing the redirection of a method invocation as requested by the
* {@link Pipe} annotation.
*/
protected static class Redirection implements AuxiliaryType, StackManipulation {
/**
* The prefix for naming fields to store method arguments.
*/
private static final String FIELD_NAME_PREFIX = "argument";
/**
* The type that declares the method for forwarding a method invocation.
*/
private final TypeDescription forwardingType;
/**
* The method that is to be forwarded.
*/
private final MethodDescription sourceMethod;
/**
* The assigner to use.
*/
private final Assigner assigner;
/**
* Determines if the generated proxy should be {@link java.io.Serializable}.
*/
private final boolean serializableProxy;
/**
* Creates a new redirection.
*
* @param forwardingType The type that declares the method for forwarding a method invocation.
* @param sourceMethod The method that is to be forwarded.
* @param assigner The assigner to use.
* @param serializableProxy Determines if the generated proxy should be {@link java.io.Serializable}.
*/
protected Redirection(TypeDescription forwardingType, MethodDescription sourceMethod, Assigner assigner, boolean serializableProxy) {
this.forwardingType = forwardingType;
this.sourceMethod = sourceMethod;
this.assigner = assigner;
this.serializableProxy = serializableProxy;
}
/**
* Extracts all parameters of a method to fields.
*
* @param methodDescription The method to extract the parameters from.
* @return A linked hash map of field names to the types of these fields representing all parameters of the
* given method.
*/
private static LinkedHashMap<String, TypeDescription> extractFields(MethodDescription methodDescription) {
TypeList parameterTypes = methodDescription.getParameters().asTypeList().asErasures();
LinkedHashMap<String, TypeDescription> typeDescriptions = new LinkedHashMap<String, TypeDescription>();
int currentIndex = 0;
for (TypeDescription parameterType : parameterTypes) {
typeDescriptions.put(fieldName(currentIndex++), parameterType);
}
return typeDescriptions;
}
/**
* Creates a new field name.
*
* @param index The index of the field.
* @return The field name that corresponds to the index.
*/
private static String fieldName(int index) {
return String.format("%s%d", FIELD_NAME_PREFIX, index);
}
@Override
public DynamicType make(String auxiliaryTypeName, ClassFileVersion classFileVersion, MethodAccessorFactory methodAccessorFactory) {
LinkedHashMap<String, TypeDescription> parameterFields = extractFields(sourceMethod);
DynamicType.Builder<?> builder = new ByteBuddy(classFileVersion).subclass(forwardingType, ConstructorStrategy.Default.NO_CONSTRUCTORS).name(auxiliaryTypeName).modifiers(DEFAULT_TYPE_MODIFIER).implement(serializableProxy ? new Class<?>[] {Serializable.class} : new Class<?>[0]).method(ElementMatchers.<MethodDescription>isAbstract().and(ElementMatchers.isDeclaredBy(forwardingType))).intercept(new MethodCall(sourceMethod, assigner)).defineConstructor().withParameters(parameterFields.values()).intercept(ConstructorCall.INSTANCE);
for (Map.Entry<String, TypeDescription> field : parameterFields.entrySet()) {
builder = builder.defineField(field.getKey(), field.getValue(), Visibility.PRIVATE);
}
return builder.make();
}
@Override
public boolean isValid() {
return true;
}
@Override
public Size apply(MethodVisitor methodVisitor, Implementation.Context implementationContext) {
TypeDescription forwardingType = implementationContext.register(this);
return new Compound(TypeCreation.of(forwardingType), Duplication.SINGLE, MethodVariableAccess.allArgumentsOf(sourceMethod), MethodInvocation.invoke(forwardingType.getDeclaredMethods().filter(ElementMatchers.isConstructor()).getOnly())).apply(methodVisitor, implementationContext);
}
/**
* The implementation to implement a
* {@link Pipe.Binder.Redirection}'s
* constructor.
*/
protected enum ConstructorCall implements Implementation {
/**
* The singleton instance.
*/
INSTANCE;
/**
* A reference of the {@link Object} type default constructor.
*/
private final MethodDescription.InDefinedShape objectTypeDefaultConstructor;
/**
* Creates the constructor call singleton.
*/
ConstructorCall() {
objectTypeDefaultConstructor = TypeDescription.OBJECT.getDeclaredMethods().filter(ElementMatchers.isConstructor()).getOnly();
}
@Override
public InstrumentedType prepare(InstrumentedType instrumentedType) {
return instrumentedType;
}
@Override
public ByteCodeAppender appender(Target implementationTarget) {
return new Appender(implementationTarget.getInstrumentedType());
}
/**
* The appender for implementing the
* {@link Pipe.Binder.Redirection.ConstructorCall}.
*/
private static class Appender implements ByteCodeAppender {
/**
* The instrumented type being created.
*/
private final TypeDescription instrumentedType;
/**
* Creates a new appender.
*
* @param instrumentedType The instrumented type that is being created.
*/
private Appender(TypeDescription instrumentedType) {
this.instrumentedType = instrumentedType;
}
@Override
public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
FieldList<?> fieldList = instrumentedType.getDeclaredFields();
StackManipulation[] fieldLoading = new StackManipulation[fieldList.size()];
int index = 0;
for (FieldDescription fieldDescription : fieldList) {
fieldLoading[index] = new StackManipulation.Compound(MethodVariableAccess.loadThis(), MethodVariableAccess.load(instrumentedMethod.getParameters().get(index)), FieldAccess.forField(fieldDescription).write());
index++;
}
StackManipulation.Size stackSize = new StackManipulation.Compound(MethodVariableAccess.loadThis(), MethodInvocation.invoke(ConstructorCall.INSTANCE.objectTypeDefaultConstructor), new StackManipulation.Compound(fieldLoading), MethodReturn.VOID).apply(methodVisitor, implementationContext);
return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof Pipe.Binder.Redirection.ConstructorCall.Appender)) return false;
final Pipe.Binder.Redirection.ConstructorCall.Appender other = (Pipe.Binder.Redirection.ConstructorCall.Appender) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$instrumentedType = this.instrumentedType;
final java.lang.Object other$instrumentedType = other.instrumentedType;
if (this$instrumentedType == null ? other$instrumentedType != null : !this$instrumentedType.equals(other$instrumentedType)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof Pipe.Binder.Redirection.ConstructorCall.Appender;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $instrumentedType = this.instrumentedType;
result = result * PRIME + ($instrumentedType == null ? 43 : $instrumentedType.hashCode());
return result;
}
}
}
/**
* The implementation to implement a
* {@link Pipe.Binder.Redirection}'s
* forwarding method.
*/
protected static class MethodCall implements Implementation {
/**
* The method that is invoked by the implemented method.
*/
private final MethodDescription redirectedMethod;
/**
* The assigner to be used for invoking the forwarded method.
*/
private final Assigner assigner;
/**
* Creates a new method call implementation.
*
* @param redirectedMethod The method that is invoked by the implemented method.
* @param assigner The assigner to be used for invoking the forwarded method.
*/
private MethodCall(MethodDescription redirectedMethod, Assigner assigner) {
this.redirectedMethod = redirectedMethod;
this.assigner = assigner;
}
@Override
public InstrumentedType prepare(InstrumentedType instrumentedType) {
return instrumentedType;
}
@Override
public ByteCodeAppender appender(Target implementationTarget) {
if (!redirectedMethod.isAccessibleTo(implementationTarget.getInstrumentedType())) {
throw new IllegalStateException("Cannot invoke " + redirectedMethod + " from outside of class via @Pipe proxy");
}
return new Appender(implementationTarget.getInstrumentedType());
}
/**
* The appender for implementing the
* {@link Pipe.Binder.Redirection.MethodCall}.
*/
private class Appender implements ByteCodeAppender {
/**
* The instrumented type that is implemented.
*/
private final TypeDescription instrumentedType;
/**
* Creates a new appender.
*
* @param instrumentedType The instrumented type to be implemented.
*/
private Appender(TypeDescription instrumentedType) {
this.instrumentedType = instrumentedType;
}
@Override
public Size apply(MethodVisitor methodVisitor, Context implementationContext, MethodDescription instrumentedMethod) {
StackManipulation thisReference = MethodVariableAccess.of(instrumentedType).loadFrom(0);
FieldList<?> fieldList = instrumentedType.getDeclaredFields();
StackManipulation[] fieldLoading = new StackManipulation[fieldList.size()];
int index = 0;
for (FieldDescription fieldDescription : fieldList) {
fieldLoading[index++] = new StackManipulation.Compound(thisReference, FieldAccess.forField(fieldDescription).read());
}
StackManipulation.Size stackSize = new StackManipulation.Compound(MethodVariableAccess.REFERENCE.loadFrom(1), assigner.assign(TypeDescription.Generic.OBJECT, redirectedMethod.getDeclaringType().asGenericType(), Assigner.Typing.DYNAMIC), new StackManipulation.Compound(fieldLoading), MethodInvocation.invoke(redirectedMethod), assigner.assign(redirectedMethod.getReturnType(), instrumentedMethod.getReturnType(), Assigner.Typing.DYNAMIC), MethodReturn.REFERENCE).apply(methodVisitor, implementationContext);
return new Size(stackSize.getMaximalSize(), instrumentedMethod.getStackSize());
}
/**
* Returns the outer instance.
*
* @return The outer instance.
*/
private MethodCall getMethodCall() {
return MethodCall.this;
}
// HE: Remove when Lombok support for getOuter is added.
@Override
public boolean equals(Object other) {
return this == other || !(other == null || getClass() != other.getClass()) && instrumentedType.equals(((Appender) other).instrumentedType) && MethodCall.this.equals(((Appender) other).getMethodCall());
}
// HE: Remove when Lombok support for getOuter is added.
@Override
public int hashCode() {
return 31 * MethodCall.this.hashCode() + instrumentedType.hashCode();
}
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof Pipe.Binder.Redirection.MethodCall)) return false;
final Pipe.Binder.Redirection.MethodCall other = (Pipe.Binder.Redirection.MethodCall) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$redirectedMethod = this.redirectedMethod;
final java.lang.Object other$redirectedMethod = other.redirectedMethod;
if (this$redirectedMethod == null ? other$redirectedMethod != null : !this$redirectedMethod.equals(other$redirectedMethod)) return false;
final java.lang.Object this$assigner = this.assigner;
final java.lang.Object other$assigner = other.assigner;
if (this$assigner == null ? other$assigner != null : !this$assigner.equals(other$assigner)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof Pipe.Binder.Redirection.MethodCall;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $redirectedMethod = this.redirectedMethod;
result = result * PRIME + ($redirectedMethod == null ? 43 : $redirectedMethod.hashCode());
final java.lang.Object $assigner = this.assigner;
result = result * PRIME + ($assigner == null ? 43 : $assigner.hashCode());
return result;
}
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof Pipe.Binder.Redirection)) return false;
final Pipe.Binder.Redirection other = (Pipe.Binder.Redirection) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$forwardingType = this.forwardingType;
final java.lang.Object other$forwardingType = other.forwardingType;
if (this$forwardingType == null ? other$forwardingType != null : !this$forwardingType.equals(other$forwardingType)) return false;
final java.lang.Object this$sourceMethod = this.sourceMethod;
final java.lang.Object other$sourceMethod = other.sourceMethod;
if (this$sourceMethod == null ? other$sourceMethod != null : !this$sourceMethod.equals(other$sourceMethod)) return false;
final java.lang.Object this$assigner = this.assigner;
final java.lang.Object other$assigner = other.assigner;
if (this$assigner == null ? other$assigner != null : !this$assigner.equals(other$assigner)) return false;
if (this.serializableProxy != other.serializableProxy) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof Pipe.Binder.Redirection;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $forwardingType = this.forwardingType;
result = result * PRIME + ($forwardingType == null ? 43 : $forwardingType.hashCode());
final java.lang.Object $sourceMethod = this.sourceMethod;
result = result * PRIME + ($sourceMethod == null ? 43 : $sourceMethod.hashCode());
final java.lang.Object $assigner = this.assigner;
result = result * PRIME + ($assigner == null ? 43 : $assigner.hashCode());
result = result * PRIME + (this.serializableProxy ? 79 : 97);
return result;
}
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public boolean equals(final java.lang.Object o) {
if (o == this) return true;
if (!(o instanceof Pipe.Binder)) return false;
final Pipe.Binder other = (Pipe.Binder) o;
if (!other.canEqual((java.lang.Object) this)) return false;
final java.lang.Object this$forwardingMethod = this.forwardingMethod;
final java.lang.Object other$forwardingMethod = other.forwardingMethod;
if (this$forwardingMethod == null ? other$forwardingMethod != null : !this$forwardingMethod.equals(other$forwardingMethod)) return false;
return true;
}
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
protected boolean canEqual(final java.lang.Object other) {
return other instanceof Pipe.Binder;
}
@java.lang.Override
@java.lang.SuppressWarnings("all")
@javax.annotation.Generated("lombok")
public int hashCode() {
final int PRIME = 59;
int result = 1;
final java.lang.Object $forwardingMethod = this.forwardingMethod;
result = result * PRIME + ($forwardingMethod == null ? 43 : $forwardingMethod.hashCode());
return result;
}
}
}