content: package io.bitchat.http.controller;
  
  
  import cn.hutool.core.util.StrUtil;
  import io.bitchat.http.util.HttpRequestUtil;
  import io.bitchat.http.HttpContext;
  import io.bitchat.http.converter.PrimitiveConverter;
  import io.bitchat.http.converter.PrimitiveTypeUtil;
  import io.bitchat.http.exception.InvocationException;
  import io.bitchat.http.exception.ValidationException;
  import lombok.extern.slf4j.Slf4j;
  import net.sf.cglib.reflect.FastClass;
  import net.sf.cglib.reflect.FastMethod;
  import org.apache.commons.beanutils.BeanUtils;
  
  import java.lang.annotation.Annotation;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  import java.lang.reflect.ParameterizedType;
  import java.lang.reflect.Type;
  import java.util.*;
  
  
  /**
   * 封装了ControllerProxy的调用过程
   *
   * @author houyi
   */
  public class ProxyInvocation {
  
      private static Invocation invocation = new Invocation();
  
      public static Object invoke(ControllerProxy proxy) throws Exception {
          Object controller = proxy.getController();
          Method method = proxy.getMethod();
          String methodName = proxy.getMethodName();
          return invocation.invoke(controller, method, methodName);
      }
  
  
      @Slf4j
      private static class Invocation {
  
          private final int KEY_VALUE_SIZE = 2;
  
          Invocation() {
  
          }
  
          /**
           * 执行方法的调用
           *
           * @param controller 控制器
           * @param method     方法
           * @param methodName 方法名
           * @return 渲染结果
           * @throws Exception 异常
           */
          Object invoke(Object controller, Method method, String methodName) throws Exception {
              if (method == null) {
                  throw new NoSuchMethodException("Can not find specified method: " + methodName);
              }
  
              Class<?> clazz = controller.getClass();
              Class<?>[] parameterTypes = method.getParameterTypes();
              Object[] parameters = null;
              Object result;
              try {
                  parameters = getParameters(method, parameterTypes);
                  // 使用 CGLib 执行反射调用
                  FastClass fastClass = FastClass.create(clazz);
                  FastMethod fastMethod = fastClass.getMethod(methodName, parameterTypes);
                  // 调用，并得到调用结果
                  result = fastMethod.invoke(controller, parameters);
  
              } catch (InvocationTargetException e) {
                  String msg = "调用出错,请求类[" + controller.getClass().getName() + "],方法名[" + method.getName() + "],参数[" + Arrays.toString(parameters) + "]";
                  throw getInvokeException(msg, e);
              }
              return result;
          }
  
          //==================================
  
          /**
           * 获得方法调用的参数
           *
           * @param method         方法
           * @param parameterTypes 方法参数类型
           * @return 参数
           * @throws Exception 参数异常
           */
          private Object[] getParameters(Method method, Class<?>[] parameterTypes) throws Exception {
              //用于存放调用参数的对象数组
              Object[] parameters = new Object[parameterTypes.length];
  
              //获得所调用方法的参数的Annotation数组
              Annotation[][] annotationArray = method.getParameterAnnotations();
  
              //获取参数列表
              Map<String, List<String>> paramMap = HttpRequestUtil.getParameterMap(HttpContext.currentContext().getRequest());
  
              //构造调用所需要的参数数组
              for (int i = 0; i < parameterTypes.length; i++) {
                  Object parameter;
                  Class<?> type = parameterTypes[i];
                  Annotation[] annotation = annotationArray[i];
                  // 如果该参数没有 Param 注解
                  if (annotation == null || annotation.length == 0) {
                      // 如果该参数类型是基础类型，则需要加 Param 注解
                      if (PrimitiveTypeUtil.isPriType(type)) {
                          log.warn("Must specify a @Param annotation for primitive type parameter in method={}", method.getName());
                          continue;
                      }
                      // 封装对象类型的parameter
                      parameter = type.newInstance();
                      BeanUtils.populate(parameter, paramMap);
                      parameters[i] = parameter;
                  } else {
                      Param param = (Param) annotation[0];
                      try {
                          // 生成当前的调用参数
                          parameter = parseParameter(paramMap, type, param, method, i);
                          if (param.notNull()) {
                              checkNull(param.key(), parameter);
                          }
                          if (param.notBlank()) {
                              checkBlank(param.key(), parameter);
                          }
                          parameters[i] = parameter;
                      } catch (Exception e) {
                          log.error("param [" + param.key() + "] is invalid，cause:" + e.getMessage());
                          throw new IllegalArgumentException("参数 " + param.key() + " 不合法：" + e.getMessage());
                      }
                  }
              }
              return parameters;
          }
  
  
          /**
           * GET 参数解析
           */
          @SuppressWarnings({"rawtypes", "unchecked"})
          private Object parseParameter(Map<String, List<String>> paramMap, Class<?> type, Param param, Method method, int index) throws InstantiationException, IllegalAccessException {
              Object value = null;
              String key = param.key();
              String defaultValue = param.defaultValue();
              if (key.length() > 0) {
                  // 如果参数是map类型
                  if (Map.class.isAssignableFrom(type)) {
                      if (index > 0) {
                          throw new ValidationException("Must have only one Map type parameter");
                      }
  
                      List<Class> types = getMethodGenericParameterTypes(method, index);
                      if (types.size() == KEY_VALUE_SIZE && (types.get(0) != String.class || types.get(1) != String.class)) {
                          throw new ValidationException("Map type parameter must both be String, Occurring Point: " + method.toGenericString());
                      }
  
                      Map<String, String> valueMap = new HashMap<String, String>(paramMap.size());
                      for (Map.Entry<String, List<String>> entry : paramMap.entrySet()) {
                          List<String> valueList = entry.getValue();
                          valueMap.put(entry.getKey(), valueList.get(0));
                      }
                      value = valueMap;
                  } else {
                      List<String> params = paramMap.get(key);
                      if (params != null) {
                          // 基础类型
                          if (PrimitiveTypeUtil.isPriType(type)) {
                              value = PrimitiveConverter.getInstance().convert(params.get(0), type);
  
                              // 数组
                          } else if (type.isArray()) {
                              String[] strArray = params.toArray(new String[]{});
                              value = PrimitiveConverter.getInstance().convert(strArray, type);
  
                              // List
                          } else if (List.class.isAssignableFrom(type)) {
                              List<Object> list;
                              List<Class> types = getMethodGenericParameterTypes(method, index);
                              Class<?> listType = types.size() == 1 ? types.get(0) : String.class;
                              if (List.class == type) {
                                  list = new ArrayList<Object>();
                              } else {
                                  list = (List<Object>) type.newInstance();
                              }
                              for (String param1 : params) {
                                  if (param1.length() > 0) {
                                      list.add(PrimitiveConverter.getInstance().convert(param1, listType));
                                  }
                              }
                              value = list;
                          }
                      } else {
                          if (PrimitiveTypeUtil.isPriType(type)) {
                              value = PrimitiveConverter.getInstance().convert(defaultValue, type);
                          }
                      }
                  }
              }
              return value;
          }
  
          /**
           * 通过反射获得Class声明的范型Class.
           * 通过反射,获得方法输入参数第index个输入参数的所有泛型参数的实际类型. 如: public void add(Map<String, Buyer> maps, List<String> names){}
           *
           * @param method 方法
           * @param index  第几个输入参数
           * @return 输入参数的泛型参数的实际类型集合, 如果没有实现ParameterizedType接口，即不支持泛型，所以直接返回空集合
           */
          @SuppressWarnings("rawtypes")
          private List<Class> getMethodGenericParameterTypes(Method method, int index) {
              List<Class> results = new ArrayList<Class>();
              Type[] genericParameterTypes = method.getGenericParameterTypes();
              if (index >= genericParameterTypes.length || index < 0) {
                  throw new RuntimeException("你输入的索引" + (index < 0 ? "不能小于0" : "超出了参数的总数"));
              }
              Type genericParameterType = genericParameterTypes[index];
              if (genericParameterType instanceof ParameterizedType) {
                  ParameterizedType aType = (ParameterizedType) genericParameterType;
                  Type[] parameterArgTypes = aType.getActualTypeArguments();
                  for (Type parameterArgType : parameterArgTypes) {
                      Class parameterArgClass = (Class) parameterArgType;
                      results.add(parameterArgClass);
                  }
                  return results;
              }
              return results;
          }
  
          /**
           * 返回调用异常
           *
           * @param msg   消息
           * @param cause 异常
           * @return 调用异常
           */
          private InvocationException getInvokeException(String msg, Throwable cause) {
              return new InvocationException(msg, cause);
          }
  
          /**
           * 断言非空
           *
           * @param dataName 参数
           * @param values   值
           */
          private void checkNull(String dataName, Object... values) {
              if (values == null) {
                  throw new ValidationException("[" + dataName + "] cannot be null");
              }
              for (Object value : values) {
                  if (value == null) {
                      throw new ValidationException("[" + dataName + "] cannot be null");
                  }
              }
          }
  
          /**
           * 断言非空
           *
           * @param dataName 参数
           * @param values   值
           */
          private void checkBlank(String dataName, Object... values) {
              if (values == null) {
                  throw new ValidationException("[" + dataName + "] cannot be null");
              }
              for (Object value : values) {
                  if (value == null || StrUtil.isBlank(value.toString())) {
                      throw new ValidationException("[" + dataName + "] cannot be blank");
                  }
              }
          }
  
      }
  
  
  }
  