/**
   * Finds a value for a given annotation in the method parameter, converts it with the given
   * builder.
   *
   * @param annotationType the annotation type.
   * @param invocation the method invocation.
   * @param returnValue the method's return value.
   * @param objectType the object type
   * @param builder the object builder.
   * @return a converted value, possibly null of the parameter was null or the {@link
   *     ObjectBuilder#build(Object, Class)} returned null;
   */
  protected <T, A extends Annotation> T findValueForType(
      Class<A> annotationType,
      MethodInvocation invocation,
      Object returnValue,
      Class<T> objectType,
      ObjectBuilder builder) {
    if (builder == null) {
      return null;
    }
    Method method = invocation.getMethod();
    int index = AopReflectionUtils.findParameterIndex(method, annotationType);
    Object[] arguments = invocation.getArguments();
    if (index != -1 && index < arguments.length) {
      Object object = arguments[index];
      return builder.build(object, objectType);
    }
    // Annotated on method, eg the return type
    A annotation = method.getAnnotation(annotationType);
    if (annotation != null) {
      return builder.build(returnValue, objectType);
    }

    // Call the builder with NULL.
    return builder.build(null, objectType);
  }
  /**
   * Runs the {@link Statement} enhancement.
   *
   * @param invocation the invocation of the method call.
   * @param returnValue the return value of the method call.
   * @param statementAnnotation the {@link Statement} annotation.
   */
  protected void processStatement(
      MethodInvocation invocation, Object returnValue, Statement statementAnnotation) {
    ObjectBuilder objectBuilder = getObjectBuilder(statementAnnotation);
    StatementBuilder statementBuilder = getStatementBuilder(statementAnnotation);
    Assert.notNull(
        objectBuilder,
        "An ObjectBuilder is required, please provide an instance of the ObjectBuilder as a bean.");
    // Required
    Actor actor =
        findValueForType(
            nl.edia.xapi.annotation.Actor.class,
            invocation,
            returnValue,
            Actor.class,
            objectBuilder);
    // Required
    Verb verb = objectBuilder.build(statementAnnotation.verb(), Verb.class);
    // Required
    IStatementObject object =
        findValueForType(
            nl.edia.xapi.annotation.StatementObject.class,
            invocation,
            returnValue,
            IStatementObject.class,
            objectBuilder);

    if (actor != null && verb != null && object != null) {
      Context context =
          findValueForType(
              nl.edia.xapi.annotation.Context.class,
              invocation,
              returnValue,
              Context.class,
              objectBuilder);
      Result result =
          findValueForType(
              nl.edia.xapi.annotation.Result.class,
              invocation,
              returnValue,
              Result.class,
              objectBuilder);
      gov.adlnet.xapi.model.Statement statement =
          statementBuilder.build(actor, verb, object, result, context, null, null);
      StatementClient statementClient = statementClientFactory.build(invocation, returnValue);
      if (statementClient != null && statement != null) {
        send(statementClient, statement);
      }
    }
  }