content: package com.github.aesteve.vertx.nubes.reflections.visitors;
  
  import com.github.aesteve.vertx.nubes.Config;
  import com.github.aesteve.vertx.nubes.annotations.Controller;
  import com.github.aesteve.vertx.nubes.annotations.filters.AfterFilter;
  import com.github.aesteve.vertx.nubes.annotations.filters.BeforeFilter;
  import com.github.aesteve.vertx.nubes.handlers.AnnotationProcessor;
  import com.github.aesteve.vertx.nubes.handlers.Processor;
  import com.github.aesteve.vertx.nubes.reflections.Filter;
  import com.github.aesteve.vertx.nubes.reflections.RouteRegistry;
  import com.github.aesteve.vertx.nubes.reflections.factories.AuthenticationFactory;
  import com.github.aesteve.vertx.nubes.routing.MVCRoute;
  import io.vertx.core.VertxException;
  import io.vertx.ext.web.Router;
  import io.vertx.ext.web.RoutingContext;
  
  import java.lang.annotation.Annotation;
  import java.lang.reflect.Method;
  import java.util.*;
  import java.util.function.BiConsumer;
  
  public class ControllerVisitor<T> extends BaseVisitor<T> {
  
    private final Method[] methods;
    final String basePath;
    final AuthenticationFactory authFactory;
    final RouteRegistry routeRegistry;
    final Map<Class<? extends Annotation>, BiConsumer<RoutingContext, ?>> returnHandlers;
    final Set<Processor> processors;
    private final Set<Filter> beforeFilters;
    private final Set<Filter> afterFilters;
  
  
    public ControllerVisitor(Class<T> controllerClass, Config config, Router router, AuthenticationFactory authFactory, RouteRegistry routeRegistry, Map<Class<? extends Annotation>, BiConsumer<RoutingContext, ?>> returnHandlers) {
      super(controllerClass, config, router);
      this.routeRegistry = routeRegistry;
      this.returnHandlers = returnHandlers;
      methods = controllerClass.getDeclaredMethods();
      Controller base = clazz.getAnnotation(Controller.class);
      basePath = base.value();
      this.authFactory = authFactory;
      processors = new LinkedHashSet<>();
      beforeFilters = new TreeSet<>();
      afterFilters = new TreeSet<>();
    }
  
    public List<MVCRoute> visit() throws IllegalAccessException, InstantiationException {
      instance = clazz.newInstance();
      List<MVCRoute> routes = new ArrayList<>();
      try {
        injectServices();
      } catch (IllegalAccessException iae) {
        throw new VertxException(iae);
      }
      extractFilters();
      for (Method method : methods) {
        MethodVisitor<T> visitor = new MethodVisitor<>(this, method);
        routes.addAll(visitor.visit());
      }
      for (MVCRoute route : routes) {
        route.addProcessorsFirst(processors);
        route.addBeforeFilters(beforeFilters);
        route.addAfterFilters(afterFilters);
      }
      return routes;
    }
  
    private void extractFilters() {
      if (hasSuperClass()) {
        ControllerVisitor<?> superClass = new ControllerVisitor<>(clazz.getSuperclass(), config, router, authFactory, routeRegistry, returnHandlers);
        superClass.extractFilters();
        beforeFilters.addAll(superClass.beforeFilters);
        afterFilters.addAll(superClass.afterFilters);
        processors.addAll(superClass.processors);
      }
      for (Method method : methods) {
        BeforeFilter beforeAnnot = method.getAnnotation(BeforeFilter.class);
        AfterFilter afterAnnot = method.getAnnotation(AfterFilter.class);
        if (beforeAnnot != null) {
          beforeFilters.add(new Filter(method, beforeAnnot));
        } else if (afterAnnot != null) {
          afterFilters.add(new Filter(method, afterAnnot));
        }
      }
      for (Annotation annotation : clazz.getDeclaredAnnotations()) {
        AnnotationProcessor<?> controllerProcessor = config.getAnnotationProcessor(annotation);
        if (controllerProcessor != null) {
          processors.add(controllerProcessor);
        }
      }
    }
  
    private boolean hasSuperClass() {
      return clazz.getSuperclass() != null && !clazz.getSuperclass().equals(Object.class);
    }
  
  }
  