package com.uas.search.aop;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import com.uas.search.annotation.NotEmpty;
import com.uas.search.util.ObjectUtils;
import com.uas.search.util.StringUtils;

/**
 * 利用AOP监测方法的参数（使用自定义注解NotEmpty）不为空
 * 
 * @author sunyj
 * @since 2017年1月3日 上午11:33:27
 */
@Aspect
@Component
public class ArgumentsCheckAspect {

	@Pointcut("execution(* com.uas..*.*(..))")
	public void checkArguments() {

	}

	@Before("checkArguments()")
	public void before(JoinPoint joinPoint) {
		Method method = getMethod(joinPoint);
		if (method == null) {
			return;
		}

		// 参数值
		Object[] args = joinPoint.getArgs();
		if (args == null || args.length < 1) {
			return;
		}
		Class<?>[] parameterTypes = method.getParameterTypes();
		// 获取所有参数注解
		Annotation[][] parameterAnnotations = method.getParameterAnnotations();
		// 遍历所有参数的注解
		for (int i = 0; i < parameterAnnotations.length; i++) {
			Object arg = args[i];
			Annotation[] annotations = parameterAnnotations[i];
			Class<?> parameterType = parameterTypes[i];
			for (int j = 0; annotations != null && j < annotations.length; j++) {
				Annotation annotation = annotations[j];
				// 如果有注解为自定义的NotEmpty注解，则对参数值进行非空判断
				if (annotation.annotationType() == NotEmpty.class) {
					NotEmpty notEmptyAnnotation = (NotEmpty) annotation;
					// 对字符串、Collection、Map进行判断
					if (ObjectUtils.isEmpty(arg)) {
						throw new IllegalArgumentException(joinPoint.getTarget().getClass().getSimpleName() + "."
								+ method.getName() + ": " + "参数为空：" + notEmptyAnnotation.value() + "("
								+ parameterType.getSimpleName() + ")");
					}
				}
			}
		}

	}

	/**
	 * 获取切点所拦截的方法
	 * 
	 * @param joinPoint
	 * @return
	 */
	private Method getMethod(JoinPoint joinPoint) {
		Method method = null;
		Object target = joinPoint.getTarget();
		Class<?> clazz = target.getClass();
		Signature signature = joinPoint.getSignature();
		// 方法全名
		String methodString = signature.toLongString();
		// 取出参数类型
		String substring = methodString.substring(methodString.indexOf("(") + 1, methodString.lastIndexOf(")"));
		try {
			if (!StringUtils.isEmpty(substring)) {
				// Class.forName()不可用于java.lang.Long[]等类似的情况
				if (!substring.contains("[]")) {
					String[] strs = substring.split(",");
					Class<?>[] clazzes = new Class[strs.length];
					for (int i = 0; i < strs.length; i++) {
						clazzes[i] = Class.forName(strs[i]);
					}
					// 根据签名获取方法
					method = clazz.getMethod(signature.getName(), clazzes);
				} else {
					// java.lang.Long[]等类似的情况下，通过比较方法名称，获取方法（可能会获取到错误的方法）
					for (Method m : clazz.getDeclaredMethods()) {
						if (m.getName().equals(signature.getName())) {
							method = m;
						}
					}
				}
			} else {
				method = clazz.getMethod(signature.getName());
			}
		} catch (NoSuchMethodException | SecurityException | ClassNotFoundException e) {
			throw new IllegalStateException("参数检查时出错", e);
		}
		return method;
	}
}
