package cn.cecep.talroad.util.effective;

import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * @author lilei
 * @create 2020-07-20 14:55
 */
@Component
public class FieldCache {
    private static final ReadWriteLock lock = new ReentrantReadWriteLock();
    private static final Lock readLock = lock.readLock();
    private static final Lock writeLock = lock.writeLock();
    private static ConcurrentHashMap<Class, ConcurrentHashMap<String, Field>> map = new ConcurrentHashMap<>();

    /**
     * map的put方法
     *
     * @param obj
     * @param fieldMap
     */
    private void put(Class obj, ConcurrentHashMap<String, Field> fieldMap) {
        try {
            map.put(obj, fieldMap);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据class对象获取map中的对应的值
     *
     * @param obj
     * @return
     */
    private ConcurrentHashMap<String, Field> getMap(Class obj) {
        try {
            readLock.lock();
            return map.get(obj) == null ? new ConcurrentHashMap<>() : map.get(obj);
        } catch (Exception e) {
            e.printStackTrace();
            return new ConcurrentHashMap<>();
        } finally {
            readLock.unlock();
        }
    }

    /**
     * 获取缓存中对象的字段,第一次获取就添加到缓存
     *
     * @param obj
     * @param fieldStr
     * @return
     */
    public Field getField(Object obj, String fieldStr) {
        if (fieldStr == null || obj == null) {
            return null;
        }
        Field field = null;
        try {
            Class clazz = obj.getClass();
            ConcurrentHashMap<String, Field> valMap = getMap(clazz);
            //获取obj对应的map缓存
            field = valMap.get(fieldStr);
            //如果缓存中没有这个field就创建一个加入缓存
            writeLock.lock();
            if (field == null) {
                Field field2 = clazz.getDeclaredField(fieldStr);
                field2.setAccessible(true);
                valMap.put(fieldStr, field2);
                //添加到缓存
                put(clazz, valMap);
                return field2;
            }
            return field;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            writeLock.unlock();
        }
    }

    /**
     * 通过字段名获取对象的值
     *
     * @param obj
     * @param fieldStr
     * @return
     */
    public Object get(Object obj, String fieldStr) {
        try {
            Field field = getField(obj, fieldStr);
            return field == null ? null : field.get(obj);
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    /**
     * set方法调用
     *
     * @param obj      设置值对象 hourData
     * @param fieldStr 设置值字段 w01001
     * @param value    要设置的值 16.32
     */
    public void set(Object obj, String fieldStr, Object value) {
        try {
            Field filed = getField(obj, fieldStr);
            if (filed != null) {
                filed.set(obj, value);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

}
