content: package com.github.aesteve.vertx.nubes.handlers;
  
  import com.github.aesteve.vertx.nubes.Config;
  import com.github.aesteve.vertx.nubes.exceptions.params.WrongParameterException;
  import com.github.aesteve.vertx.nubes.reflections.injectors.annot.AnnotatedParamInjector;
  import com.github.aesteve.vertx.nubes.reflections.injectors.annot.AnnotatedParamInjectorRegistry;
  import com.github.aesteve.vertx.nubes.reflections.injectors.typed.ParamInjector;
  import com.github.aesteve.vertx.nubes.reflections.injectors.typed.TypedParamInjectorRegistry;
  import io.vertx.core.Handler;
  import io.vertx.core.http.HttpServerResponse;
  import io.vertx.ext.web.RoutingContext;
  
  import java.lang.annotation.Annotation;
  import java.lang.reflect.Method;
  import java.lang.reflect.Parameter;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.function.BiConsumer;
  
  public abstract class AbstractMethodInvocationHandler<T> implements Handler<RoutingContext> {
  
    protected final Method method;
    protected final Object instance;
    protected final boolean hasNext;
    protected final BiConsumer<RoutingContext, T> returnHandler;
    protected final boolean returnsSomething;
    private final Config config;
    private final Parameter[] parameters;
    protected boolean usesRoutingContext;
    protected boolean usesHttpResponse;
  
    protected AbstractMethodInvocationHandler(Object instance, Method method, Config config, boolean hasNext, BiConsumer<RoutingContext, T> returnHandler) {
      this.method = method;
      returnsSomething = !method.getReturnType().equals(Void.TYPE);
      this.hasNext = hasNext;
      parameters = method.getParameters();
      for (Parameter param : parameters) {
        Class<?> paramType = param.getType();
        if (paramType.equals(RoutingContext.class)) {
          usesRoutingContext = true;
        }
        if (paramType.equals(HttpServerResponse.class)) {
          usesHttpResponse = true;
        }
      }
      this.config = config;
      this.instance = instance;
      this.returnHandler = returnHandler;
    }
  
    @Override
    abstract public void handle(RoutingContext routingContext);
  
    protected Object[] getParameters(RoutingContext routingContext) throws WrongParameterException {
      List<Object> params = new ArrayList<>();
      for (Parameter param : parameters) {
        Object paramInstance = getParameterInstance(routingContext, param.getAnnotations(), param.getType(), param.getName());
        params.add(paramInstance);
      }
      return params.toArray();
    }
  
    @SuppressWarnings({"rawtypes", "unchecked"})
    private Object getParameterInstance(RoutingContext context, Annotation[] annotations, Class<?> parameterClass, String paramName) throws WrongParameterException {
      final TypedParamInjectorRegistry typeInjectors = config.getTypeInjectors();
      if (annotations.length == 0) { // rely on type
        final ParamInjector<?> injector = typeInjectors.getInjector(parameterClass);
        if (injector == null) {
          return null;
        }
        return injector.resolve(context);
      }
      if (annotations.length > 1) {
        throw new IllegalArgumentException("Every parameter should only have ONE annotation");
      }
      final Annotation annotation = annotations[0]; // rely on annotation
      final AnnotatedParamInjectorRegistry annotatedInjectors = config.getAnnotatedInjectors();
      final AnnotatedParamInjector injector = annotatedInjectors.getInjector(annotation.annotationType());
      if (injector == null) {
        return null;
      }
      return injector.resolve(context, annotation, paramName, parameterClass);
    }
  }