1 /*
2 * Copyright 2014 - 2020 Rafael Winterhalter
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package net.bytebuddy.implementation.bind.annotation;
17
18 import net.bytebuddy.description.annotation.AnnotationDescription;
19 import net.bytebuddy.description.field.FieldDescription;
20 import net.bytebuddy.description.method.MethodDescription;
21 import net.bytebuddy.description.method.MethodList;
22 import net.bytebuddy.description.method.ParameterDescription;
23 import net.bytebuddy.description.type.TypeDescription;
24 import net.bytebuddy.implementation.Implementation;
25 import net.bytebuddy.implementation.bind.MethodDelegationBinder;
26 import net.bytebuddy.implementation.bytecode.StackManipulation;
27 import net.bytebuddy.implementation.bytecode.assign.Assigner;
28 import net.bytebuddy.implementation.bytecode.member.FieldAccess;
29 import net.bytebuddy.implementation.bytecode.member.MethodVariableAccess;
30
31 import java.lang.annotation.*;
32
33 import static net.bytebuddy.matcher.ElementMatchers.named;
34
35 /**
36 * <p>
37 * Assigns the value of a field of the instrumented type to the annotated parameter. For a binding to be valid,
38 * the instrumented type must be able to access a field of the given name. Also, the parameter's type must be
39 * assignable to the given field. For attempting a type casting, the {@link RuntimeType} annotation can be
40 * applied to the parameter.
41 * </p>
42 * <p>
43 * Setting {@link FieldValue#value()} is optional. If the value is not set, the field value attempts to bind a setter's
44 * or getter's field if the intercepted method is an accessor method. Otherwise, the binding renders the target method
45 * to be an illegal candidate for binding.
46 * </p>
47 *
48 * @see net.bytebuddy.implementation.MethodDelegation
49 * @see net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder
50 * @see net.bytebuddy.implementation.bind.annotation.RuntimeType
51 */
52 @Documented
53 @Retention(RetentionPolicy.RUNTIME)
54 @Target(ElementType.PARAMETER)
55 public @interface FieldValue {
56
57 /**
58 * The name of the field to be accessed.
59 *
60 * @return The name of the field.
61 */
62 String value() default TargetMethodAnnotationDrivenBinder.ParameterBinder.ForFieldBinding.BEAN_PROPERTY;
63
64 /**
65 * Defines the type on which the field is declared. If this value is not set, the most specific type's field is read,
66 * if two fields with the same name exist in the same type hierarchy.
67 *
68 * @return The type that declares the accessed field.
69 */
70 Class<?> declaringType() default void.class;
71
72 /**
73 * Binds a {@link FieldValue} annotation.
74 */
75 enum Binder implements TargetMethodAnnotationDrivenBinder.ParameterBinder<FieldValue> {
76
77 /**
78 * The singleton instance.
79 */
80 INSTANCE(new Delegate());
81
82 /**
83 * The annotation method that for the defining type.
84 */
85 private static final MethodDescription.InDefinedShape DECLARING_TYPE;
86
87 /**
88 * The annotation method for the field's name.
89 */
90 private static final MethodDescription.InDefinedShape FIELD_NAME;
91
92 /*
93 * Initializes the methods of the annotation that is read by this binder.
94 */
95 static {
96 MethodList<MethodDescription.InDefinedShape> methodList = TypeDescription.ForLoadedType.of(FieldValue.class).getDeclaredMethods();
97 DECLARING_TYPE = methodList.filter(named("declaringType")).getOnly();
98 FIELD_NAME = methodList.filter(named("value")).getOnly();
99 }
100
101 /**
102 * A delegate parameter binder responsible for binding the parameter.
103 */
104 private final TargetMethodAnnotationDrivenBinder.ParameterBinder<FieldValue> delegate;
105
106 /**
107 * Creates a new binder for a {@link FieldValue}.
108 *
109 * @param delegate A delegate parameter binder responsible for binding the parameter.
110 */
111 Binder(TargetMethodAnnotationDrivenBinder.ParameterBinder<FieldValue> delegate) {
112 this.delegate = delegate;
113 }
114
115 /**
116 * {@inheritDoc}
117 */
118 public Class<FieldValue> getHandledType() {
119 return delegate.getHandledType();
120 }
121
122 /**
123 * {@inheritDoc}
124 */
125 public MethodDelegationBinder.ParameterBinding<?> bind(AnnotationDescription.Loadable<FieldValue> annotation,
126 MethodDescription source,
127 ParameterDescription target,
128 Implementation.Target implementationTarget,
129 Assigner assigner,
130 Assigner.Typing typing) {
131 return delegate.bind(annotation, source, target, implementationTarget, assigner, typing);
132 }
133
134 /**
135 * A delegate implementation for the {@link FieldValue.Binder}.
136 */
137 protected static class Delegate extends ForFieldBinding<FieldValue> {
138
139 /**
140 * {@inheritDoc}
141 */
142 public Class<FieldValue> getHandledType() {
143 return FieldValue.class;
144 }
145
146 @Override
147 protected String fieldName(AnnotationDescription.Loadable<FieldValue> annotation) {
148 return annotation.getValue(FIELD_NAME).resolve(String.class);
149 }
150
151 @Override
152 protected TypeDescription declaringType(AnnotationDescription.Loadable<FieldValue> annotation) {
153 return annotation.getValue(DECLARING_TYPE).resolve(TypeDescription.class);
154 }
155
156 @Override
157 protected MethodDelegationBinder.ParameterBinding<?> bind(FieldDescription fieldDescription,
158 AnnotationDescription.Loadable<FieldValue> annotation,
159 MethodDescription source,
160 ParameterDescription target,
161 Implementation.Target implementationTarget,
162 Assigner assigner) {
163 StackManipulation stackManipulation = new StackManipulation.Compound(
164 fieldDescription.isStatic()
165 ? StackManipulation.Trivial.INSTANCE
166 : MethodVariableAccess.loadThis(),
167 FieldAccess.forField(fieldDescription).read(),
168 assigner.assign(fieldDescription.getType(), target.getType(), RuntimeType.Verifier.check(target))
169 );
170 return stackManipulation.isValid()
171 ? new MethodDelegationBinder.ParameterBinding.Anonymous(stackManipulation)
172 : MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE;
173 }
174 }
175 }
176 }
177