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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
19 import net.bytebuddy.build.HashCodeAndEqualsPlugin;
20 import net.bytebuddy.description.annotation.AnnotationDescription;
21 import net.bytebuddy.description.field.FieldDescription;
22 import net.bytebuddy.description.method.MethodDescription;
23 import net.bytebuddy.description.method.ParameterDescription;
24 import net.bytebuddy.description.type.TypeDescription;
25 import net.bytebuddy.dynamic.scaffold.FieldLocator;
26 import net.bytebuddy.implementation.Implementation;
27 import net.bytebuddy.implementation.bind.MethodDelegationBinder;
28 import net.bytebuddy.implementation.bytecode.StackManipulation;
29 import net.bytebuddy.implementation.bytecode.assign.Assigner;
30 import net.bytebuddy.implementation.bytecode.constant.*;
31 import net.bytebuddy.utility.JavaConstant;
32 import net.bytebuddy.utility.JavaType;
33
34 import java.lang.annotation.Annotation;
35 import java.util.*;
36
37 import static net.bytebuddy.matcher.ElementMatchers.isGetter;
38 import static net.bytebuddy.matcher.ElementMatchers.isSetter;
39
40 /**
41 * This {@link net.bytebuddy.implementation.bind.MethodDelegationBinder} binds
42 * method by analyzing annotations found on the <i>target</i> method that is subject to a method binding.
43 */
44 @HashCodeAndEqualsPlugin.Enhance
45 public class TargetMethodAnnotationDrivenBinder implements MethodDelegationBinder {
46
47 /**
48 * The processor for performing an actual method delegation.
49 */
50 private final DelegationProcessor delegationProcessor;
51
52 /**
53 * Creates a new target method annotation-driven binder.
54 *
55 * @param delegationProcessor The delegation processor to use.
56 */
57 protected TargetMethodAnnotationDrivenBinder(DelegationProcessor delegationProcessor) {
58 this.delegationProcessor = delegationProcessor;
59 }
60
61 /**
62 * Creates a new method delegation binder that binds method based on annotations found on the target method.
63 *
64 * @param parameterBinders A list of parameter binder delegates. Each such delegate is responsible for creating a
65 * {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.ParameterBinding}
66 * for a specific annotation.
67 * @return An appropriate method delegation binder.
68 */
69 public static MethodDelegationBinder of(List<? extends ParameterBinder<?>> parameterBinders) {
70 return new TargetMethodAnnotationDrivenBinder(DelegationProcessor.of(parameterBinders));
71 }
72
73 /**
74 * {@inheritDoc}
75 */
76 public MethodDelegationBinder.Record compile(MethodDescription candidate) {
77 if (IgnoreForBinding.Verifier.check(candidate)) {
78 return MethodDelegationBinder.Record.Illegal.INSTANCE;
79 }
80 List<DelegationProcessor.Handler> handlers = new ArrayList<DelegationProcessor.Handler>(candidate.getParameters().size());
81 for (ParameterDescription parameterDescription : candidate.getParameters()) {
82 handlers.add(delegationProcessor.prepare(parameterDescription));
83 }
84 return new Record(candidate, handlers, RuntimeType.Verifier.check(candidate));
85 }
86
87 /**
88 * A compiled record of a target method annotation-driven binder.
89 */
90 @HashCodeAndEqualsPlugin.Enhance
91 protected static class Record implements MethodDelegationBinder.Record {
92
93 /**
94 * The candidate method.
95 */
96 private final MethodDescription candidate;
97
98 /**
99 * A list of handlers for each parameter.
100 */
101 private final List<DelegationProcessor.Handler> handlers;
102
103 /**
104 * The typing to apply.
105 */
106 private final Assigner.Typing typing;
107
108 /**
109 * Creates a default compiled method delegation binder.
110 *
111 * @param candidate The candidate method.
112 * @param handlers A list of handlers for each parameter.
113 * @param typing The typing to apply.
114 */
115 protected Record(MethodDescription candidate, List<DelegationProcessor.Handler> handlers, Assigner.Typing typing) {
116 this.candidate = candidate;
117 this.handlers = handlers;
118 this.typing = typing;
119 }
120
121 /**
122 * {@inheritDoc}
123 */
124 public MethodBinding bind(Implementation.Target implementationTarget,
125 MethodDescription source,
126 MethodDelegationBinder.TerminationHandler terminationHandler,
127 MethodInvoker methodInvoker,
128 Assigner assigner) {
129 if (!candidate.isAccessibleTo(implementationTarget.getInstrumentedType())) {
130 return MethodBinding.Illegal.INSTANCE;
131 }
132 StackManipulation methodTermination = terminationHandler.resolve(assigner, typing, source, candidate);
133 if (!methodTermination.isValid()) {
134 return MethodBinding.Illegal.INSTANCE;
135 }
136 MethodBinding.Builder methodDelegationBindingBuilder = new MethodBinding.Builder(methodInvoker, candidate);
137 for (DelegationProcessor.Handler handler : handlers) {
138 ParameterBinding<?> parameterBinding = handler.bind(source, implementationTarget, assigner);
139 if (!parameterBinding.isValid() || !methodDelegationBindingBuilder.append(parameterBinding)) {
140 return MethodBinding.Illegal.INSTANCE;
141 }
142 }
143 return methodDelegationBindingBuilder.build(methodTermination);
144 }
145
146 @Override
147 public String toString() {
148 return candidate.toString();
149 }
150 }
151
152 /**
153 * A parameter binder is used as a delegate for binding a parameter according to a particular annotation type found
154 * on this parameter.
155 *
156 * @param <T> The {@link java.lang.annotation.Annotation#annotationType()} handled by this parameter binder.
157 */
158 @SuppressFBWarnings(value = "IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION", justification = "Safe initialization is implied")
159 public interface ParameterBinder<T extends Annotation> {
160
161 /**
162 * The default parameter binders to be used.
163 */
164 List<ParameterBinder<?>> DEFAULTS = Collections.unmodifiableList(Arrays.<TargetMethodAnnotationDrivenBinder.ParameterBinder<?>>asList(
165 Argument.Binder.INSTANCE,
166 AllArguments.Binder.INSTANCE,
167 Origin.Binder.INSTANCE,
168 This.Binder.INSTANCE,
169 Super.Binder.INSTANCE,
170 Default.Binder.INSTANCE,
171 SuperCall.Binder.INSTANCE,
172 DefaultCall.Binder.INSTANCE,
173 SuperMethod.Binder.INSTANCE,
174 DefaultMethod.Binder.INSTANCE,
175 FieldValue.Binder.INSTANCE,
176 StubValue.Binder.INSTANCE,
177 Empty.Binder.INSTANCE));
178
179 /**
180 * The annotation type that is handled by this parameter binder.
181 *
182 * @return The {@link java.lang.annotation.Annotation#annotationType()} handled by this parameter binder.
183 */
184 Class<T> getHandledType();
185
186 /**
187 * Creates a parameter binding for the given target parameter.
188 *
189 * @param annotation The annotation that was cause for the delegation to this argument binder.
190 * @param source The intercepted source method.
191 * @param target Tge target parameter that is subject to be bound to
192 * intercepting the {@code source} method.
193 * @param implementationTarget The target of the current implementation that is subject to this binding.
194 * @param assigner An assigner that can be used for applying the binding.
195 * @param typing The typing to apply.
196 * @return A parameter binding for the requested target method parameter.
197 */
198 ParameterBinding<?> bind(AnnotationDescription.Loadable<T> annotation,
199 MethodDescription source,
200 ParameterDescription target,
201 Implementation.Target implementationTarget,
202 Assigner assigner,
203 Assigner.Typing typing);
204
205 /**
206 * <p>
207 * Implements a parameter binder that binds a fixed value to a parameter with a given annotation.
208 * </p>
209 * <p>
210 * This binder is only capable to store values that can either be expressed as Java byte code or as a constant pool value. This
211 * includes primitive types, {@link String} values, {@link Class} values which can also be expressed as {@link TypeDescription}
212 * instances or method handles and method types for classes of a version at least of Java 7. The latter instances can also be
213 * expressed as unloaded {@link JavaConstant} representations.
214 * </p>
215 * <p>
216 * <b>Important</b>: When supplying a method handle or a method type, all types that are implied must be visible to the instrumented
217 * type or an {@link IllegalAccessException} will be thrown at runtime.
218 * </p>
219 *
220 * @param <S> The bound annotation's type.
221 */
222 abstract class ForFixedValue<S extends Annotation> implements ParameterBinder<S> {
223
224 /**
225 * {@inheritDoc}
226 */
227 public ParameterBinding<?> bind(AnnotationDescription.Loadable<S> annotation,
228 MethodDescription source,
229 ParameterDescription target,
230 Implementation.Target implementationTarget,
231 Assigner assigner,
232 Assigner.Typing typing) {
233 Object value = bind(annotation, source, target);
234 if (value == null) {
235 return new ParameterBinding.Anonymous(DefaultValue.of(target.getType()));
236 }
237 StackManipulation stackManipulation;
238 TypeDescription suppliedType;
239 if (value instanceof Boolean) {
240 stackManipulation = IntegerConstant.forValue((Boolean) value);
241 suppliedType = TypeDescription.ForLoadedType.of(boolean.class);
242 } else if (value instanceof Byte) {
243 stackManipulation = IntegerConstant.forValue((Byte) value);
244 suppliedType = TypeDescription.ForLoadedType.of(byte.class);
245 } else if (value instanceof Short) {
246 stackManipulation = IntegerConstant.forValue((Short) value);
247 suppliedType = TypeDescription.ForLoadedType.of(short.class);
248 } else if (value instanceof Character) {
249 stackManipulation = IntegerConstant.forValue((Character) value);
250 suppliedType = TypeDescription.ForLoadedType.of(char.class);
251 } else if (value instanceof Integer) {
252 stackManipulation = IntegerConstant.forValue((Integer) value);
253 suppliedType = TypeDescription.ForLoadedType.of(int.class);
254 } else if (value instanceof Long) {
255 stackManipulation = LongConstant.forValue((Long) value);
256 suppliedType = TypeDescription.ForLoadedType.of(long.class);
257 } else if (value instanceof Float) {
258 stackManipulation = FloatConstant.forValue((Float) value);
259 suppliedType = TypeDescription.ForLoadedType.of(float.class);
260 } else if (value instanceof Double) {
261 stackManipulation = DoubleConstant.forValue((Double) value);
262 suppliedType = TypeDescription.ForLoadedType.of(double.class);
263 } else if (value instanceof String) {
264 stackManipulation = new TextConstant((String) value);
265 suppliedType = TypeDescription.STRING;
266 } else if (value instanceof Class) {
267 stackManipulation = ClassConstant.of(TypeDescription.ForLoadedType.of((Class<?>) value));
268 suppliedType = TypeDescription.CLASS;
269 } else if (value instanceof TypeDescription) {
270 stackManipulation = ClassConstant.of((TypeDescription) value);
271 suppliedType = TypeDescription.CLASS;
272 } else if (JavaType.METHOD_HANDLE.isInstance(value)) {
273 stackManipulation = new JavaConstantValue(JavaConstant.MethodHandle.ofLoaded(value));
274 suppliedType = JavaType.METHOD_HANDLE.getTypeStub();
275 } else if (value instanceof JavaConstant.MethodHandle) {
276 stackManipulation = new JavaConstantValue((JavaConstant.MethodHandle) value);
277 suppliedType = JavaType.METHOD_HANDLE.getTypeStub();
278 } else if (JavaType.METHOD_TYPE.isInstance(value)) {
279 stackManipulation = new JavaConstantValue(JavaConstant.MethodType.ofLoaded(value));
280 suppliedType = JavaType.METHOD_HANDLE.getTypeStub();
281 } else if (value instanceof JavaConstant.MethodType) {
282 stackManipulation = new JavaConstantValue((JavaConstant.MethodType) value);
283 suppliedType = JavaType.METHOD_HANDLE.getTypeStub();
284 } else {
285 throw new IllegalStateException("Not able to save in class's constant pool: " + value);
286 }
287 return new ParameterBinding.Anonymous(new StackManipulation.Compound(
288 stackManipulation,
289 assigner.assign(suppliedType.asGenericType(), target.getType(), typing)
290 ));
291 }
292
293 /**
294 * Resolves a value for the given annotation on a parameter that is processed by a {@link net.bytebuddy.implementation.MethodDelegation}.
295 *
296 * @param annotation The annotation that triggered this binding.
297 * @param source The method for which a delegation is currently bound.
298 * @param target The parameter for which a value is bound.
299 * @return The constant pool value that is bound to this parameter or {@code null} for binding this value.
300 */
301 protected abstract Object bind(AnnotationDescription.Loadable<S> annotation, MethodDescription source, ParameterDescription target);
302
303 /**
304 * <p>
305 * A parameter binder that binds a fixed value to a parameter annotation when using a {@link net.bytebuddy.implementation.MethodDelegation}.
306 * </p>
307 * <p>
308 * This binder is only capable to store
309 * values that can either be expressed as Java byte code or as a constant pool value. This includes primitive types, {@link String} values,
310 * {@link Class} values which can also be expressed as {@link TypeDescription} instances or method handles and method types for classes of
311 * a version at least of Java 7. The latter instances can also be expressed as unloaded {@link JavaConstant} representations.
312 * </p>
313 *
314 * @param <U> The bound annotation's type.
315 */
316 @HashCodeAndEqualsPlugin.Enhance
317 public static class OfConstant<U extends Annotation> extends ForFixedValue<U> {
318
319 /**
320 * The type of the annotation that is bound by this binder.
321 */
322 private final Class<U> type;
323
324 /**
325 * The value that is assigned to any annotated parameter.
326 */
327 @HashCodeAndEqualsPlugin.ValueHandling(HashCodeAndEqualsPlugin.ValueHandling.Sort.REVERSE_NULLABILITY)
328 private final Object value;
329
330 /**
331 * Creates a binder for binding a fixed value to a parameter annotated with the given annotation.
332 *
333 * @param type The type of the annotation that is bound by this binder.
334 * @param value The value that is assigned to any annotated parameter.
335 */
336 protected OfConstant(Class<U> type, Object value) {
337 this.type = type;
338 this.value = value;
339 }
340
341 /**
342 * Creates a binder for binding a fixed value to a given annotation.
343 *
344 * @param type The type of the annotation that is bound by this binder.
345 * @param value The value that is assigned to any annotated parameter.
346 * @param <V> The bound annotation's type.
347 * @return A parameter binder that binds the given annotation to the supplied value.
348 */
349 public static <V extends Annotation> ParameterBinder<V> of(Class<V> type, Object value) {
350 return new OfConstant<V>(type, value);
351 }
352
353 /**
354 * {@inheritDoc}
355 */
356 public Class<U> getHandledType() {
357 return type;
358 }
359
360 @Override
361 protected Object bind(AnnotationDescription.Loadable<U> annotation, MethodDescription source, ParameterDescription target) {
362 return value;
363 }
364 }
365 }
366
367 /**
368 * A parameter binder that binds a field's value.
369 *
370 * @param <S> The {@link java.lang.annotation.Annotation#annotationType()} handled by this parameter binder.
371 */
372 abstract class ForFieldBinding<S extends Annotation> implements ParameterBinder<S> {
373
374 /**
375 * Indicates that a name should be extracted from an accessor method.
376 */
377 protected static final String BEAN_PROPERTY = "";
378
379 /**
380 * Resolves a field locator for a potential accessor method.
381 *
382 * @param fieldLocator The field locator to use.
383 * @param methodDescription The method description that is the potential accessor.
384 * @return A resolution for a field locator.
385 */
386 private static FieldLocator.Resolution resolveAccessor(FieldLocator fieldLocator, MethodDescription methodDescription) {
387 String fieldName;
388 if (isSetter().matches(methodDescription)) {
389 fieldName = methodDescription.getInternalName().substring(3);
390 } else if (isGetter().matches(methodDescription)) {
391 fieldName = methodDescription.getInternalName().substring(methodDescription.getInternalName().startsWith("is") ? 2 : 3);
392 } else {
393 return FieldLocator.Resolution.Illegal.INSTANCE;
394 }
395 return fieldLocator.locate(Character.toLowerCase(fieldName.charAt(0)) + fieldName.substring(1));
396 }
397
398 /**
399 * {@inheritDoc}
400 */
401 public ParameterBinding<?> bind(AnnotationDescription.Loadable<S> annotation,
402 MethodDescription source,
403 ParameterDescription target,
404 Implementation.Target implementationTarget,
405 Assigner assigner,
406 Assigner.Typing typing) {
407 if (!declaringType(annotation).represents(void.class)) {
408 if (declaringType(annotation).isPrimitive() || declaringType(annotation).isArray()) {
409 throw new IllegalStateException("A primitive type or array type cannot declare a field: " + source);
410 } else if (!implementationTarget.getInstrumentedType().isAssignableTo(declaringType(annotation))) {
411 return MethodDelegationBinder.ParameterBinding.Illegal.INSTANCE;
412 }
413 }
414 FieldLocator fieldLocator = declaringType(annotation).represents(void.class)
415 ? new FieldLocator.ForClassHierarchy(implementationTarget.getInstrumentedType())
416 : new FieldLocator.ForExactType(declaringType(annotation), implementationTarget.getInstrumentedType());
417 FieldLocator.Resolution resolution = fieldName(annotation).equals(BEAN_PROPERTY)
418 ? resolveAccessor(fieldLocator, source)
419 : fieldLocator.locate(fieldName(annotation));
420 return resolution.isResolved() && !(source.isStatic() && !resolution.getField().isStatic())
421 ? bind(resolution.getField(), annotation, source, target, implementationTarget, assigner)
422 : ParameterBinding.Illegal.INSTANCE;
423 }
424
425 /**
426 * Extracts the field name from an annotation.
427 *
428 * @param annotation The annotation from which to extract the field name.
429 * @return The field name defined by the handled annotation.
430 */
431 protected abstract String fieldName(AnnotationDescription.Loadable<S> annotation);
432
433 /**
434 * Extracts the declaring type from an annotation.
435 *
436 * @param annotation The annotation from which to extract the declaring type.
437 * @return The declaring type defined by the handled annotation.
438 */
439 protected abstract TypeDescription declaringType(AnnotationDescription.Loadable<S> annotation);
440
441 /**
442 * Creates a parameter binding for the given target parameter.
443 *
444 * @param fieldDescription The field for which this binder binds a value.
445 * @param annotation The annotation that was cause for the delegation to this argument binder.
446 * @param source The intercepted source method.
447 * @param target Tge target parameter that is subject to be bound to
448 * intercepting the {@code source} method.
449 * @param implementationTarget The target of the current implementation that is subject to this binding.
450 * @param assigner An assigner that can be used for applying the binding.
451 * @return A parameter binding for the requested target method parameter.
452 */
453 protected abstract ParameterBinding<?> bind(FieldDescription fieldDescription,
454 AnnotationDescription.Loadable<S> annotation,
455 MethodDescription source,
456 ParameterDescription target,
457 Implementation.Target implementationTarget,
458 Assigner assigner);
459 }
460 }
461
462 /**
463 * A delegation processor is a helper class for a
464 * {@link net.bytebuddy.implementation.bind.annotation.TargetMethodAnnotationDrivenBinder}
465 * for performing its actual logic. By outsourcing this logic to this helper class, a cleaner implementation
466 * can be provided.
467 */
468 @HashCodeAndEqualsPlugin.Enhance
469 protected static class DelegationProcessor {
470
471 /**
472 * A map of registered annotation types to the binder that is responsible for binding a parameter
473 * that is annotated with the given annotation.
474 */
475 private final Map<? extends TypeDescription, ? extends ParameterBinder<?>> parameterBinders;
476
477 /**
478 * Creates a new delegation processor.
479 *
480 * @param parameterBinders A mapping of parameter binders by their handling type.
481 */
482 protected DelegationProcessor(Map<? extends TypeDescription, ? extends ParameterBinder<?>> parameterBinders) {
483 this.parameterBinders = parameterBinders;
484 }
485
486 /**
487 * Creates a new delegation processor.
488 *
489 * @param parameterBinders A list of parameter binder delegates. Each such delegate is responsible for creating
490 * a {@link net.bytebuddy.implementation.bind.MethodDelegationBinder.ParameterBinding}
491 * for a specific annotation.
492 * @return A corresponding delegation processor.
493 */
494 protected static DelegationProcessor of(List<? extends ParameterBinder<?>> parameterBinders) {
495 Map<TypeDescription, ParameterBinder<?>> parameterBinderMap = new HashMap<TypeDescription, ParameterBinder<?>>();
496 for (ParameterBinder<?> parameterBinder : parameterBinders) {
497 if (parameterBinderMap.put(TypeDescription.ForLoadedType.of(parameterBinder.getHandledType()), parameterBinder) != null) {
498 throw new IllegalArgumentException("Attempt to bind two handlers to " + parameterBinder.getHandledType());
499 }
500 }
501 return new DelegationProcessor(parameterBinderMap);
502 }
503
504 /**
505 * Locates a handler which is responsible for processing the given parameter. If no explicit handler can
506 * be located, a fallback handler is provided.
507 *
508 * @param target The target parameter being handled.
509 * @return A handler for processing the parameter with the given annotations.
510 */
511 protected Handler prepare(ParameterDescription target) {
512 Assigner.Typing typing = RuntimeType.Verifier.check(target);
513 Handler handler = new Handler.Unbound(target, typing);
514 for (AnnotationDescription annotation : target.getDeclaredAnnotations()) {
515 ParameterBinder<?> parameterBinder = parameterBinders.get(annotation.getAnnotationType());
516 if (parameterBinder != null && handler.isBound()) {
517 throw new IllegalStateException("Ambiguous binding for parameter annotated with two handled annotation types");
518 } else if (parameterBinder != null /* && !handler.isBound() */) {
519 handler = Handler.Bound.of(target, parameterBinder, annotation, typing);
520 }
521 }
522 return handler;
523 }
524
525 /**
526 * A handler is responsible for processing a parameter's binding.
527 */
528 protected interface Handler {
529
530 /**
531 * Indicates if this handler was explicitly bound.
532 *
533 * @return {@code true} if this handler was explicitly bound.
534 */
535 boolean isBound();
536
537 /**
538 * Handles a parameter binding.
539 *
540 * @param source The intercepted source method.
541 * @param implementationTarget The target of the current implementation.
542 * @param assigner The assigner to use.
543 * @return A parameter binding that reflects the given arguments.
544 */
545 ParameterBinding<?> bind(MethodDescription source, Implementation.Target implementationTarget, Assigner assigner);
546
547 /**
548 * An unbound handler is a fallback for returning an illegal binding for parameters for which no parameter
549 * binder could be located.
550 */
551 @HashCodeAndEqualsPlugin.Enhance
552 class Unbound implements Handler {
553
554 /**
555 * The target parameter being handled.
556 */
557 private final ParameterDescription target;
558
559 /**
560 * The typing to apply.
561 */
562 private final Assigner.Typing typing;
563
564 /**
565 * Creates a new unbound handler.
566 *
567 * @param target The target parameter being handled.
568 * @param typing The typing to apply.
569 */
570 protected Unbound(ParameterDescription target, Assigner.Typing typing) {
571 this.target = target;
572 this.typing = typing;
573 }
574
575 /**
576 * {@inheritDoc}
577 */
578 public boolean isBound() {
579 return false;
580 }
581
582 /**
583 * {@inheritDoc}
584 */
585 public ParameterBinding<?> bind(MethodDescription source, Implementation.Target implementationTarget, Assigner assigner) {
586 return Argument.Binder.INSTANCE.bind(AnnotationDescription.ForLoadedAnnotation.<Argument>of(new DefaultArgument(target.getIndex())),
587 source,
588 target,
589 implementationTarget,
590 assigner,
591 typing);
592 }
593
594 /**
595 * A default implementation of an {@link net.bytebuddy.implementation.bind.annotation.Argument} annotation.
596 */
597 protected static class DefaultArgument implements Argument {
598
599 /**
600 * The name of the value annotation parameter.
601 */
602 private static final String VALUE = "value";
603
604 /**
605 * The name of the value binding mechanic parameter.
606 */
607 private static final String BINDING_MECHANIC = "bindingMechanic";
608
609 /**
610 * The index of the source method parameter to be bound.
611 */
612 private final int parameterIndex;
613
614 /**
615 * Creates a new instance of an argument annotation.
616 *
617 * @param parameterIndex The index of the source method parameter to be bound.
618 */
619 protected DefaultArgument(int parameterIndex) {
620 this.parameterIndex = parameterIndex;
621 }
622
623 /**
624 * {@inheritDoc}
625 */
626 public int value() {
627 return parameterIndex;
628 }
629
630 /**
631 * {@inheritDoc}
632 */
633 public BindingMechanic bindingMechanic() {
634 return BindingMechanic.UNIQUE;
635 }
636
637 /**
638 * {@inheritDoc}
639 */
640 public Class<Argument> annotationType() {
641 return Argument.class;
642 }
643
644 @Override
645 public int hashCode() {
646 return ((127 * BINDING_MECHANIC.hashCode()) ^ BindingMechanic.UNIQUE.hashCode()) + ((127 * VALUE.hashCode()) ^ parameterIndex);
647 }
648
649 @Override
650 public boolean equals(Object other) {
651 return this == other || other instanceof Argument && parameterIndex == ((Argument) other).value();
652 }
653
654 @Override
655 public String toString() {
656 return "@" + Argument.class.getName()
657 + "(bindingMechanic=" + BindingMechanic.UNIQUE.toString()
658 + ", value=" + parameterIndex + ")";
659 }
660 }
661 }
662
663 /**
664 * A bound handler represents an unambiguous parameter binder that was located for a given array of
665 * annotations.
666 *
667 * @param <T> The annotation type of a given handler.
668 */
669 @HashCodeAndEqualsPlugin.Enhance
670 class Bound<T extends Annotation> implements Handler {
671
672 /**
673 * The target parameter being handled.
674 */
675 private final ParameterDescription target;
676
677 /**
678 * The parameter binder that is actually responsible for binding the parameter.
679 */
680 private final ParameterBinder<T> parameterBinder;
681
682 /**
683 * The annotation value that lead to the binding of this handler.
684 */
685 private final AnnotationDescription.Loadable<T> annotation;
686
687 /**
688 * The typing to apply.
689 */
690 private final Assigner.Typing typing;
691
692 /**
693 * Creates a new bound handler.
694 *
695 * @param target The target parameter being handled.
696 * @param parameterBinder The parameter binder that is actually responsible for binding the parameter.
697 * @param annotation The annotation value that lead to the binding of this handler.
698 * @param typing The typing to apply.
699 */
700 protected Bound(ParameterDescription target,
701 ParameterBinder<T> parameterBinder,
702 AnnotationDescription.Loadable<T> annotation,
703 Assigner.Typing typing) {
704 this.target = target;
705 this.parameterBinder = parameterBinder;
706 this.annotation = annotation;
707 this.typing = typing;
708 }
709
710 /**
711 * Creates a handler for a given annotation.
712 *
713 * @param target The target parameter being handled.
714 * @param parameterBinder The parameter binder that should process an annotation.
715 * @param annotation An annotation instance that can be understood by this parameter binder.
716 * @param typing The typing to apply.
717 * @return A handler for processing the given annotation.
718 */
719 @SuppressWarnings("unchecked")
720 protected static Handler of(ParameterDescription target,
721 ParameterBinder<?> parameterBinder,
722 AnnotationDescription annotation,
723 Assigner.Typing typing) {
724 return new Bound<Annotation>(target,
725 (ParameterBinder<Annotation>) parameterBinder,
726 (AnnotationDescription.Loadable<Annotation>) annotation.prepare(parameterBinder.getHandledType()),
727 typing);
728 }
729
730 /**
731 * {@inheritDoc}
732 */
733 public boolean isBound() {
734 return true;
735 }
736
737 /**
738 * {@inheritDoc}
739 */
740 public ParameterBinding<?> bind(MethodDescription source, Implementation.Target implementationTarget, Assigner assigner) {
741 return parameterBinder.bind(annotation,
742 source,
743 target,
744 implementationTarget,
745 assigner,
746 typing);
747 }
748 }
749 }
750 }
751 }
752