All Downloads are FREE. Search and download functionalities are using the official Maven repository.

shz.core.FieldSetter Maven / Gradle / Ivy

There is a newer version: 2024.0.2
Show newest version
package shz.core;

import shz.core.cl.ClassLoaderHelp;
import shz.core.constant.NullConstant;
import shz.core.enums.EnumHelp;
import shz.core.enums.IEnum;
import shz.core.time.*;
import shz.core.type.TypeHelp;

import java.lang.reflect.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URL;
import java.sql.Timestamp;
import java.time.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.DoubleAdder;
import java.util.concurrent.atomic.LongAdder;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

@SuppressWarnings("unchecked")
public final class FieldSetter {
    private FieldSetter() {
        throw new IllegalStateException();
    }

    public static Object parse(Object value, Class cls) {
        if (value == null || cls == null) return null;
        Object parse = parse0(value, cls);
        return parse == null || parse == NullConstant.OBJECT ? null : parse;
    }

    public static  T cast(Object value, Class cls) {
        Object parse = parse(value, cls);
        return parse == null ? null : (T) parse;
    }

    private static Object parse0(Object value, Class cls) {
        if (cls != Object.class && cls.isInstance(value)) return value;
        String s;
        if (Enum.class.isAssignableFrom(cls)) {
            List enums;
            if ((enums = EnumHelp.enumSet(cls)).isEmpty()) return null;
            s = value.toString();
            if (IEnum.class.isAssignableFrom(cls)) {
                IEnum c = null, v = null;
                for (Object obj : enums) {
                    IEnum e = (IEnum) obj;
                    if (s.equalsIgnoreCase(e.name())) return e;
                    if (c == null) if (s.equals(e.getCode())) c = e;
                    if (v == null) if (s.equals(e.getValue())) v = e;
                }
                return c == null ? v : c;
            }
            return enums.stream().map(e -> (Enum) e).filter(e -> e.name().equalsIgnoreCase(s)).findFirst().orElse(null);
        }
        String name = cls.getName();
        try {
            switch (name) {
                case "java.lang.String":
                    if (value instanceof Timestamp) return TimeHelp.format((Timestamp) value);
                    if (value instanceof ZonedDateTime) return TimeHelp.format((ZonedDateTime) value);
                    if (value instanceof LocalDateTime) return TimeHelp.format((LocalDateTime) value);
                    if (value instanceof Instant) return TimeHelp.format((Instant) value);
                    if (value instanceof LocalDate) return TimeHelp.format((LocalDate) value);
                    if (value instanceof Date) return TimeHelp.format((Date) value);
                    if (value instanceof LocalTime) return TimeHelp.format((LocalTime) value);
                    return value.toString();
                case "java.lang.Boolean":
                case "boolean":
                    s = value.toString();
                    if (s.equals("0")) return false;
                    if (s.equals("1")) return true;
                    return Boolean.parseBoolean(s);
                case "java.lang.Byte":
                case "byte":
                    s = value.toString();
                    if (s.equalsIgnoreCase("true")) return (byte) 1;
                    else if (s.equalsIgnoreCase("false")) return (byte) 0;
                    return Byte.parseByte(s);
                case "java.lang.Character":
                case "char":
                    s = value.toString();
                    return s.length() > 0 ? s.charAt(0) : '\0';
                case "java.lang.Short":
                case "short":
                    s = value.toString();
                    if (s.equalsIgnoreCase("true")) return (short) 1;
                    else if (s.equalsIgnoreCase("false")) return (short) 0;
                    return Short.parseShort(s);
                case "java.lang.Integer":
                case "int":
                    s = value.toString();
                    if (s.equalsIgnoreCase("true")) return 1;
                    else if (s.equalsIgnoreCase("false")) return 0;
                    return Integer.parseInt(s);
                case "java.lang.Long":
                case "long":
                    s = value.toString();
                    if (s.equalsIgnoreCase("true")) return 1L;
                    else if (s.equalsIgnoreCase("false")) return 0L;
                    return Long.parseLong(s);
                case "java.lang.Double":
                case "double":
                    return Double.parseDouble(value.toString());
                case "java.lang.Float":
                case "float":
                    return Float.parseFloat(value.toString());
                case "java.net.URI":
                    return URI.create(value.toString());
                case "java.net.URL":
                    return new URL(value.toString());
                case "java.util.Locale":
                    s = value.toString().trim();
                    String[] arr = s.indexOf(' ') != -1 ? s.split(" ") : s.split(",");
                    if (arr.length == 3) return new Locale(arr[0], arr[1], arr[2]);
                    if (arr.length == 2) return new Locale(arr[0], arr[1]);
                    return new Locale(s);
                case "java.lang.Class":
                    return ClassLoaderHelp.load(value.toString());
                case "java.math.BigInteger":
                    return new BigInteger(value.toString());
                case "java.math.BigDecimal":
                    return new BigDecimal(value.toString());
                case "java.util.concurrent.atomic.AtomicInteger":
                    return new AtomicInteger(Integer.parseInt(value.toString()));
                case "java.util.concurrent.atomic.AtomicLong":
                    return new AtomicLong(Long.parseLong(value.toString()));
                case "java.util.concurrent.atomic.LongAdder":
                    long lv = Long.parseLong(value.toString());
                    LongAdder longAdder = new LongAdder();
                    longAdder.add(lv);
                    return longAdder;
                case "java.util.concurrent.atomic.DoubleAdder":
                    double dv = Double.parseDouble(value.toString());
                    DoubleAdder doubleAdder = new DoubleAdder();
                    doubleAdder.add(dv);
                    return doubleAdder;
                case "java.sql.Timestamp":
                    return ToTimestamp.from(value);
                case "java.time.ZonedDateTime":
                    return ToZonedDateTime.from(value);
                case "java.time.LocalDateTime":
                    return ToLocalDateTime.from(value);
                case "java.time.Instant":
                    return ToInstant.from(value);
                case "java.time.LocalDate":
                    return ToLocalDate.from(value);
                case "java.util.Date":
                    return ToDate.from(value);
                case "java.time.LocalTime":
                    return ToLocalTime.from(value);
                default:
                    return NullConstant.OBJECT;
            }
        } catch (Exception e) {
            return null;
        }
    }

    public static boolean set(Field field, Object obj, Object value, Type type, Function> sourceMapper, BiFunction valueMapper) {
        if (field == null || obj == null || value == null) return false;
        if (type == null) type = field.getDeclaringClass().getGenericSuperclass();
        return set0(field, obj, value, type, sourceMapper == null ? ToMap::getSimple : sourceMapper, valueMapper == null ? (f, v) -> v : valueMapper);
    }

    private static boolean set0(Field field, Object obj, Object value, Type type, Function> sourceMapper, BiFunction valueMapper) {
        value = valueMapper.apply(field, value);
        if (value == null) return false;
        Map fieldTypeMap = TypeHelp.fieldType(type);
        Type fType = TypeHelp.fieldType(field, fieldTypeMap);
        Class cls = TypeHelp.fieldClass(field, fieldTypeMap);

        if (List.class.isAssignableFrom(cls)) return setList(field, obj, value, fType, sourceMapper, valueMapper);
        if (Set.class.isAssignableFrom(cls)) return setSet(field, obj, value, fType, sourceMapper, valueMapper);
        if (Map.class.isAssignableFrom(cls)) return setMap(field, obj, value, fType, sourceMapper, valueMapper);
        if (cls.isArray()) return setArray(field, obj, value, fType, sourceMapper, valueMapper);
        if (Collection.class.isAssignableFrom(cls)) return false;

        Object parse;
        if ((parse = parse0(value, cls)) == null) return false;
        if (parse != NullConstant.OBJECT) return AccessibleHelp.setField(field, obj, parse);
        return setObject(field, obj, value, cls, fType, sourceMapper, valueMapper);
    }

    private static boolean setList(Field field, Object obj, Object value, Type type, Function> sourceMapper, BiFunction valueMapper) {
        Type[] types = TypeHelp.getActualTypeArguments(type);
        Type eType = types.length == 1 ? types[0] : null;
        Class cls = cls(eType);

        Class srcCls = value.getClass();
        if (Collection.class.isAssignableFrom(srcCls)) {
            Collection collection = (Collection) value;
            if (collection.isEmpty()) return false;
            List list = new ArrayList<>(collection.size());
            Iterator iterator = collection.iterator();
            Object create;
            while (iterator.hasNext()) {
                Object next = iterator.next();
                if (next == null) continue;
                create = create0(next, cls, eType, sourceMapper, valueMapper);
                if (create != null) list.add(create);
            }
            if (list.isEmpty()) return false;
            return AccessibleHelp.setField(field, obj, list);
        }

        if (srcCls.isArray()) {
            int len = Array.getLength(value);
            if (len == 0) return false;
            List list = new ArrayList<>(len);
            Object create;
            for (int i = 0; i < len; ++i) {
                Object e = Array.get(value, i);
                if (e == null) continue;
                create = create0(e, cls, eType, sourceMapper, valueMapper);
                if (create != null) list.add(create);
            }
            if (list.isEmpty()) return false;
            return AccessibleHelp.setField(field, obj, list);
        }

        Object result = create0(value, cls, eType, sourceMapper, valueMapper);
        if (result == null) return false;

        List list = new ArrayList<>();
        list.add(result);
        return AccessibleHelp.setField(field, obj, list);
    }

    private static boolean setSet(Field field, Object obj, Object value, Type type, Function> sourceMapper, BiFunction valueMapper) {
        Type[] types = TypeHelp.getActualTypeArguments(type);
        Type eType = types.length == 1 ? types[0] : null;
        Class cls = cls(eType);

        Class srcCls = value.getClass();
        if (Collection.class.isAssignableFrom(srcCls)) {
            Collection collection = (Collection) value;
            if (collection.isEmpty()) return false;
            Set set = ToSet.get(collection.size(), 0, true).build();
            Iterator iterator = collection.iterator();
            Object create;
            while (iterator.hasNext()) {
                Object next = iterator.next();
                if (next == null) continue;
                create = create0(next, cls, eType, sourceMapper, valueMapper);
                if (create != null) set.add(create);
            }
            if (set.isEmpty()) return false;
            return AccessibleHelp.setField(field, obj, set);
        }

        if (srcCls.isArray()) {
            int len = Array.getLength(value);
            if (len == 0) return false;
            Set set = ToSet.get(len, 0, true).build();
            Object create;
            for (int i = 0; i < len; ++i) {
                Object e = Array.get(value, i);
                if (e == null) continue;
                create = create0(e, cls, eType, sourceMapper, valueMapper);
                if (create != null) set.add(create);
            }
            if (set.isEmpty()) return false;
            return AccessibleHelp.setField(field, obj, set);
        }

        Object result = create0(value, cls, eType, sourceMapper, valueMapper);
        if (result == null) return false;

        Set set = ToSet.get(16, 0, true).build();
        set.add(result);
        return AccessibleHelp.setField(field, obj, set);
    }

    private static boolean setMap(Field field, Object obj, Object value, Type type, Function> sourceMapper, BiFunction valueMapper) {
        Map map = ToMap.getSimple(value);
        if (map.isEmpty()) return false;

        Type[] types = TypeHelp.getActualTypeArguments(type);
        Type kType, vType;
        if (types.length == 2) {
            kType = types[0];
            vType = types[1];
        } else {
            kType = null;
            vType = null;
        }
        Class kCls = cls(kType), vCls = cls(vType);

        Map result = ToMap.get(map.size(), 0, true).build();
        Object k, v;
        for (Map.Entry entry : map.entrySet()) {
            String key = entry.getKey();
            if (key == null) continue;
            Object val = entry.getValue();
            if (val == null) continue;
            k = create0(key, kCls, kType, sourceMapper, valueMapper);
            if (k == null) continue;
            v = create0(val, vCls, vType, sourceMapper, valueMapper);
            if (v == null) continue;
            result.put(k, v);
        }

        if (result.isEmpty()) return false;
        return AccessibleHelp.setField(field, obj, result);
    }

    private static boolean setArray(Field field, Object obj, Object value, Type type, Function> sourceMapper, BiFunction valueMapper) {
        Class cls = cls(type).getComponentType();
        if (type instanceof GenericArrayType)
            while ((type = ((GenericArrayType) type).getGenericComponentType()) instanceof GenericArrayType) ;

        Object array;
        Class srcCls = value.getClass();
        if (Collection.class.isAssignableFrom(srcCls)) {
            Collection collection = (Collection) value;
            if (collection.isEmpty()) return false;
            array = Array.newInstance(cls, collection.size());
            Iterator iterator = collection.iterator();
            int i = -1;
            Object create;
            while (iterator.hasNext()) {
                ++i;
                Object next = iterator.next();
                if (next == null) continue;
                create = create0(next, cls, type, sourceMapper, valueMapper);
                if (create != null) Array.set(array, i, create);
            }
        } else if (srcCls.isArray()) array = value;
        else {
            Object result = create0(value, cls, type, sourceMapper, valueMapper);
            if (result == null) return false;
            array = Array.newInstance(cls, 1);
            Array.set(array, 0, result);
        }
        return AccessibleHelp.setField(field, obj, array);
    }

    private static boolean setObject(Field field, Object obj, Object value, Class cls, Type type, Function> sourceMapper, BiFunction valueMapper) {
        Object des = AccessibleHelp.newInstance(cls);
        if (des == null) return false;
        setObject(value, des, cls, type, sourceMapper, valueMapper);
        return AccessibleHelp.setField(field, obj, des);
    }

    private static Class cls(Type type) {
        Class cls = TypeHelp.toClass(type);
        return cls == null ? Object.class : cls;
    }

    public static boolean set(Field field, Object obj, Object value, Type type) {
        return set(field, obj, value, type, null, null);
    }

    public static boolean set(Field field, Object obj, Object value, BiFunction valueMapper) {
        return set(field, obj, value, null, null, valueMapper);
    }

    public static boolean set(Field field, Object obj, Object value) {
        return set(field, obj, value, null, null, null);
    }

    public static  T create(Object src, Class cls, Type type, Function> sourceMapper, BiFunction valueMapper) {
        if (src == null || cls == null) return null;
        if (type == null) type = cls.getGenericSuperclass();
        return create0(src, cls, type, sourceMapper == null ? ToMap::getSimple : sourceMapper, valueMapper == null ? (f, v) -> v : valueMapper);
    }

    private static  T create0(Object src, Class cls, Type type, Function> sourceMapper, BiFunction valueMapper) {
        if (TypeHelp.likeCommon(cls)) {
            Class srcCls = src.getClass();
            if (Map.class.isAssignableFrom(srcCls)) {
                Map map = (Map) src;
                if (map.size() != 1) return null;
                src = map.values().iterator().next();
            } else if (Collection.class.isAssignableFrom(srcCls)) {
                Collection collection = (Collection) src;
                if (collection.size() != 1) return null;
                src = collection.iterator().next();
            } else if (srcCls.isArray()) {
                if (Array.getLength(src) != 1) return null;
                src = Array.get(src, 0);
            }
            src = valueMapper.apply(null, src);
            if (src == null) return null;
            Object parse = parse0(src, cls);
            return parse == null || parse == NullConstant.OBJECT ? null : (T) parse;
        }
        T des;
        if ((des = AccessibleHelp.newInstance(cls)) == null) return null;
        if (!set0(src, des, cls, type, sourceMapper, valueMapper)) return null;
        return des;
    }

    private static boolean set0(Object src, Object des, Class cls, Type type, Function> sourceMapper, BiFunction valueMapper) {
        if (Collection.class.isAssignableFrom(cls)) return setCollection(src, des);
        if (Map.class.isAssignableFrom(cls)) return setMap(src, des);
        if (cls.isArray()) return setArray(src, des);
        return setObject(src, des, cls, type, sourceMapper, valueMapper);
    }

    private static boolean setCollection(Object src, Object des) {
        try {
            Class srcCls = src.getClass();
            if (Collection.class.isAssignableFrom(srcCls)) {
                Collection collection = (Collection) src;
                if (collection.isEmpty()) return false;
                ((Collection) des).addAll(collection);
                return true;
            }
            if (srcCls.isArray()) {
                int len = Array.getLength(src);
                if (len == 0) return false;
                Collection collection = (Collection) des;
                for (int i = 0; i < len; ++i) collection.add(Array.get(src, i));
                return true;
            }
            ((Collection) des).add(src);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private static boolean setMap(Object src, Object des) {
        Map map = ToMap.getSimple(src);
        if (map.isEmpty()) return false;
        try {
            ((Map) des).putAll(map);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    private static boolean setArray(Object src, Object des) {
        return false;
    }

    private static boolean setObject(Object src, Object des, Class cls, Type type, Function> sourceMapper, BiFunction valueMapper) {
        Map source = sourceMapper.apply(src);
        if (NullHelp.isEmpty(source)) return false;
        boolean mark = false;
        for (Field field : AccessibleHelp.fields(cls)) {
            Object value = source.get(field.getName());
            if (value == null) continue;
            mark |= set0(field, des, value, type, sourceMapper, valueMapper);
        }
        return mark;
    }

    public static  T create(Object src, Class cls, Type type) {
        return create(src, cls, type, null, null);
    }

    public static  T create(Object src, Class cls, BiFunction valueMapper) {
        return create(src, cls, null, null, valueMapper);
    }

    public static  T create(Object src, Class cls) {
        return create(src, cls, null, null, null);
    }

    public static  List creates(Collection dataset, Class cls, Type type, Function> sourceMapper, BiFunction valueMapper) {
        if (NullHelp.isEmpty(dataset)) return Collections.emptyList();
        if (type == null) type = cls.getGenericSuperclass();
        sourceMapper = sourceMapper == null ? ToMap::getSimple : sourceMapper;
        valueMapper = valueMapper == null ? (f, v) -> v : valueMapper;
        List result = new ArrayList<>(dataset.size());
        T t;
        for (Object src : dataset) {
            if (src == null) continue;
            t = create0(src, cls, type, sourceMapper, valueMapper);
            if (t == null) continue;
            result.add(t);
        }
        return result.isEmpty() ? Collections.emptyList() : result;
    }

    public static  List creates(Collection dataset, Class cls, Type type) {
        return creates(dataset, cls, type, null, null);
    }

    public static  List creates(Collection dataset, Class cls, BiFunction valueMapper) {
        return creates(dataset, cls, null, null, valueMapper);
    }

    public static  List creates(Collection dataset, Class cls) {
        return creates(dataset, cls, null, null, null);
    }

    public static  T copy(Object src, T des, Type type, Function> sourceMapper, BiFunction valueMapper) {
        if (des == null) return null;
        if (src == null) return des;
        Class cls = des.getClass();
        if (!TypeHelp.likeModel(cls)) return des;
        if (type == null) type = cls.getGenericSuperclass();
        set0(src, des, cls, type, sourceMapper == null ? ToMap::getSimple : sourceMapper, valueMapper == null ? (f, v) -> v : valueMapper);
        return des;
    }

    public static  T copy(Object src, T des, Type type) {
        return copy(src, des, type, null, null);
    }

    public static  T copy(Object src, T des, BiFunction valueMapper) {
        return copy(src, des, null, null, valueMapper);
    }

    public static  T copy(Object src, T des) {
        return copy(src, des, null, null, null);
    }

    public static  List copies(Collection dataset, Supplier supplier, Type type, Function> sourceMapper, BiFunction valueMapper) {
        if (NullHelp.isEmpty(dataset) || supplier == null) return Collections.emptyList();
        sourceMapper = sourceMapper == null ? ToMap::getSimple : sourceMapper;
        valueMapper = valueMapper == null ? (f, v) -> v : valueMapper;
        List result = new ArrayList<>(dataset.size());
        boolean mark = false;
        T t;
        Class cls = null;
        for (Object src : dataset) {
            t = supplier.get();
            if (t == null) break;
            if (!mark) {
                cls = t.getClass();
                if (!TypeHelp.likeModel(cls)) break;
                if (type == null) type = cls.getGenericSuperclass();
                mark = true;
            }
            result.add(t);
            if (src == null) continue;
            set0(src, t, cls, type, sourceMapper, valueMapper);
        }
        return result.isEmpty() ? Collections.emptyList() : result;
    }

    public static  List copies(Collection dataset, Supplier supplier, Type type) {
        return copies(dataset, supplier, type, null, null);
    }

    public static  List copies(Collection dataset, Supplier supplier, BiFunction valueMapper) {
        return copies(dataset, supplier, null, null, valueMapper);
    }

    public static  List copies(Collection dataset, Supplier supplier) {
        return copies(dataset, supplier, null, null, null);
    }

    public static boolean simpleSet(Field field, Object obj, Object value, Type type) {
        if (field == null || obj == null || value == null) return false;
        if (type == null) type = field.getDeclaringClass().getGenericSuperclass();
        Object parse = parse0(value, TypeHelp.fieldClass(field, type));
        return parse != null && AccessibleHelp.setField(field, obj, parse == NullConstant.OBJECT ? value : parse);
    }

    public static boolean simpleSet(Field field, Object obj, Object value) {
        return simpleSet(field, obj, value, null);
    }

    public static  T simpleCopy(Object src, T des) {
        if (des == null) return null;
        if (src == null) return des;
        Class cls = des.getClass();
        if (!TypeHelp.likeModel(cls)) return des;
        Map simple = ToMap.getSimple(src);
        if (simple.isEmpty()) return des;
        AccessibleHelp.fields(cls, field -> {
            Object value = simple.get(field.getName());
            if (value == null) return false;
            Class fieldClass = TypeHelp.fieldClass(field, field.getDeclaringClass().getGenericSuperclass());
            if (!TypeHelp.likeCommon(fieldClass)) return false;
            Object parse = parse0(value, fieldClass);
            if (parse != null) AccessibleHelp.setField(field, des, parse == NullConstant.OBJECT ? value : parse);
            return false;
        });
        return des;
    }
}