package com.ilivoo.my.utility.classreading.filter;

import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.filter.AbstractClassTestingTypeFilter;
import org.springframework.util.Assert;

import com.ilivoo.my.utility.ArrayUtils;

public class ClassPackageFilter extends AbstractClassTestingTypeFilter{
	private final String childPackage;
	
	private final boolean isDeep;
	
	public ClassPackageFilter(String childPackage){
		this(childPackage, true);
	}
	
	/**
	 * 因为当我们查找的时候就已经提供了基本的包了， 所以找到的类肯定是基本包中的类
	 * @param childPackage 子包名， 如： "utility"， 不能使用 "core.utility"这样的， 并且子包名必须完整
	 * @param isDeep 是否是深层次， 也就是是否是"com.ilivoo.**.utility.**.class"  两个"**"表示是有多层关系的
	 */
	public ClassPackageFilter(String childPackage, boolean isDeep){
		Assert.notNull(childPackage, "子包不能为空");
		if(childPackage.contains(".")){
			/*
			 * 从这里我们可以学习到原来运行时异常是不会要求我们捕获的， 因为运行时的错误是必须停止的， 而不能恢复程序。
			 */
			throw new IllegalArgumentException("子包名必须唯一， 并且使用'utility'这样独立的子包名");
		}
		this.childPackage = childPackage;
		this.isDeep = isDeep;
	}

	@Override
	protected boolean match(ClassMetadata metadata) {
		boolean isPattern = isPatternPackage(metadata.getClassName());
		return isPattern;
	}
	
	/**
	 * 通过基本的类名判断是否符合规则
	 * @param className 类名
	 * @return
	 */
	private boolean isPatternPackage(String className){
		Assert.notNull(className, "className不能为空");
		String[] names = className.split("\\.");
		/*
		 * com.ilivoo.**.childPackage.**
		 * 这里处理最前面的情况， 假设这个childPackage="com", 那么当然就肯定是符合的
		 * 假设包名和类名设计得不合理， 首字母大小写颠倒会出现问题， 所以这里也要配置
		 * 
		 * 一般情况下我们不能直接使用name.length-1作为数组的下标， 因为这样可能会抛出下标-1, 从而报错。 但是前面我们判断了className是否为空
		 */
		if(names[0].equals(childPackage) || names[names.length-1].equals(childPackage)){
			return true;
		}
		if(ArrayUtils.hasInArray(childPackage, names, false)){
			if(isDeep){
				return true;
			}else{
				int classIndex = names.length-1;
				int childPackageIndex = ArrayUtils.indexInArray(childPackage, names, false);
				if(classIndex-childPackageIndex <= 1){
					return true;
				}
			}
		}
		return false;
	}

}
