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

com.landawn.abacus.util.Maps Maven / Gradle / Ivy

/*
 * Copyright (C) 2019 HaiYang Li
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed under the License
 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing permissions and limitations under
 * the License.
 */

package com.landawn.abacus.util;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;

import com.landawn.abacus.DirtyMarker;
import com.landawn.abacus.util.Fn.Suppliers;
import com.landawn.abacus.util.u.Nullable;
import com.landawn.abacus.util.function.BiFunction;
import com.landawn.abacus.util.function.IntFunction;
import com.landawn.abacus.util.function.Supplier;

/**
 * 
 * @since 1.3
 * 
 * @author Haiyang Li
 */
public final class Maps {
    private Maps() {
        // Utility class.
    }

    public static  Map newMap(Collection c, final Try.Function keyMapper) throws E {
        N.checkArgNotNull(keyMapper);

        if (N.isNullOrEmpty(c)) {
            return new HashMap();
        }

        final Map result = new HashMap<>(N.initHashCapacity(c.size()));

        for (T e : c) {
            result.put(keyMapper.apply(e), e);
        }

        return result;
    }

    public static  Map newLinkedHashMap(Collection c, final Try.Function keyMapper)
            throws E {
        N.checkArgNotNull(keyMapper);

        if (N.isNullOrEmpty(c)) {
            return new LinkedHashMap();
        }

        final Map result = new LinkedHashMap<>(N.initHashCapacity(c.size()));

        for (T e : c) {
            result.put(keyMapper.apply(e), e);
        }

        return result;
    }

    public static  Map newMap(Collection c,
            final Try.Function keyMapper, final Try.Function valueExtractor) throws E, E2 {
        N.checkArgNotNull(keyMapper);
        N.checkArgNotNull(valueExtractor);

        if (N.isNullOrEmpty(c)) {
            return new HashMap();
        }

        final Map result = new HashMap<>(N.initHashCapacity(c.size()));

        for (T e : c) {
            result.put(keyMapper.apply(e), valueExtractor.apply(e));
        }

        return result;
    }

    public static , E extends Exception, E2 extends Exception> M newMap(Collection c,
            final Try.Function keyMapper, final Try.Function valueExtractor,
            final IntFunction mapSupplier) throws E, E2 {
        N.checkArgNotNull(keyMapper);
        N.checkArgNotNull(valueExtractor);

        if (N.isNullOrEmpty(c)) {
            return mapSupplier.apply(0);
        }

        final M result = mapSupplier.apply(c.size());

        for (T e : c) {
            result.put(keyMapper.apply(e), valueExtractor.apply(e));
        }

        return result;
    }

    @SuppressWarnings("rawtypes")
    static Map newTargetMap(Map m) {
        return newTargetMap(m, m == null ? 0 : m.size());
    }

    @SuppressWarnings("rawtypes")
    static Map newTargetMap(Map m, int size) {
        if (m == null) {
            return new HashMap<>();
        }

        Map res = null;

        if (HashMap.class.equals(m.getClass())) {
            res = new HashMap<>(N.initHashCapacity(size));
        } else if (m instanceof SortedMap) {
            res = new TreeMap<>(((SortedMap) m).comparator());
        } else if (m instanceof IdentityHashMap) {
            res = new IdentityHashMap<>(N.initHashCapacity(size));
        } else if (m instanceof LinkedHashMap) {
            res = new LinkedHashMap<>(N.initHashCapacity(size));
        } else if (m instanceof ImmutableMap) {
            res = new LinkedHashMap<>(N.initHashCapacity(size));
        } else {
            try {
                res = N.newInstance(m.getClass());
            } catch (Exception e) {
                res = new LinkedHashMap<>(N.initHashCapacity(size));
            }
        }

        return res;
    }

    @SuppressWarnings("rawtypes")
    static Map newOrderingMap(Map m) {
        if (m == null) {
            return new HashMap<>();
        }

        Map res = null;

        if (HashMap.class.equals(m.getClass())) {
            res = new HashMap<>(N.initHashCapacity(m.size()));
        } else if (m instanceof SortedMap) {
            res = new LinkedHashMap<>(N.initHashCapacity(m.size()));
        } else if (m instanceof IdentityHashMap) {
            res = new IdentityHashMap<>(N.initHashCapacity(m.size()));
        } else if (m instanceof LinkedHashMap) {
            res = new LinkedHashMap<>(N.initHashCapacity(m.size()));
        } else if (m instanceof ImmutableMap) {
            res = new LinkedHashMap<>(N.initHashCapacity(m.size()));
        } else {
            try {
                res = N.newInstance(m.getClass());
            } catch (Exception e) {
                res = new LinkedHashMap<>(N.initHashCapacity(m.size()));
            }
        }

        return res;
    }

    public static  Nullable get(final Map map, final Object key) {
        if (N.isNullOrEmpty(map)) {
            return Nullable.empty();
        }

        final V val = map.get(key);

        if (val != null || map.containsKey(key)) {
            return Nullable.of(val);
        } else {
            return Nullable.empty();
        }
    }

    /**
     * Returns a list of values of the keys which exist in the specified Map.
     * If the key dosn't exist in the Map, No value will be added into the returned list. 
     * 
     * @param map
     * @param keys
     * @return
     */
    public static  List getIfPresentForEach(final Map map, final Collection keys) {
        if (N.isNullOrEmpty(map) || N.isNullOrEmpty(keys)) {
            return new ArrayList<>(0);
        }

        final List result = new ArrayList<>(keys.size());
        V val = null;

        for (Object key : keys) {
            val = map.get(key);

            if (val != null || map.containsKey(key)) {
                result.add(val);
            }
        }

        return result;
    }

    /**
     * Returns the value to which the specified key is mapped, or
     * {@code defaultValue} if this map contains no mapping for the key.
     * 
     * @param map
     * @param key
     * @param defaultValue
     * @return
     */
    public static  V getOrDefault(final Map map, final Object key, final V defaultValue) {
        if (N.isNullOrEmpty(map)) {
            return defaultValue;
        }

        final V val = map.get(key);

        if (val != null || map.containsKey(key)) {
            return val;
        } else {
            return defaultValue;
        }
    }

    /**
     * Returns the value to which the specified key is mapped, or
     * an empty immutable {@code List} if this map contains no mapping for the key.
     * 
     * @param map
     * @param key
     * @return
     */
    public static > List getOrEmptyList(final Map map, final Object key) {
        if (N.isNullOrEmpty(map)) {
            return N. emptyList();
        }

        final V val = map.get(key);

        if (val != null || map.containsKey(key)) {
            return val;
        } else {
            return N.emptyList();
        }
    }

    /**
     * Returns the value to which the specified key is mapped, or
     * an empty immutable {@code Set} if this map contains no mapping for the key.
     * 
     * @param map
     * @param key
     * @return
     */
    public static > Set getOrEmptySet(final Map map, final Object key) {
        if (N.isNullOrEmpty(map)) {
            return N. emptySet();
        }

        final V val = map.get(key);

        if (val != null || map.containsKey(key)) {
            return val;
        } else {
            return N.emptySet();
        }
    }

    public static  List getOrDefaultForEach(final Map map, final Collection keys, final V defaultValue) {
        if (N.isNullOrEmpty(keys)) {
            return new ArrayList<>(0);
        } else if (N.isNullOrEmpty(map)) {
            return new ArrayList<>(Arrays.asList(Array.repeat(defaultValue, keys.size())));
        }

        final List result = new ArrayList<>(keys.size());
        V val = null;

        for (Object key : keys) {
            val = map.get(key);

            if (val != null || map.containsKey(key)) {
                result.add(val);
            } else {
                result.add(defaultValue);
            }
        }

        return result;
    }

    /**
     * Returns the value associated with the specified {@code key} if it exists in the specified {@code map} contains, or the new put {@code List} if it's absent.
     * 
     * @param map
     * @param key
     * @return
     */
    public static  List getAndPutListIfAbsent(final Map> map, final K key) {
        List v = map.get(key);

        if (v == null) {
            v = new ArrayList<>();
            v = map.put(key, v);
        }

        return v;
    }

    /**
     * Returns the value associated with the specified {@code key} if it exists in the specified {@code map} contains, or the new put {@code Set} if it's absent.
     * 
     * @param map
     * @param key
     * @return
     */
    public static  Set getAndPutSetIfAbsent(final Map> map, final K key) {
        Set v = map.get(key);

        if (v == null) {
            v = new HashSet<>();
            v = map.put(key, v);
        }

        return v;
    }

    /**
     * Returns the value associated with the specified {@code key} if it exists in the specified {@code map} contains, or the new put {@code Set} if it's absent.
     * 
     * @param map
     * @param key
     * @return
     */
    public static  Set getAndPutLinkedHashSetIfAbsent(final Map> map, final K key) {
        Set v = map.get(key);

        if (v == null) {
            v = new LinkedHashSet<>();
            v = map.put(key, v);
        }

        return v;
    }

    /**
     * Returns the value associated with the specified {@code key} if it exists in the specified {@code map} contains, or the new put {@code Map} if it's absent.
     * 
     * @param map
     * @param key
     * @return
     */
    public static  Map getAndPutMapIfAbsent(final Map> map, final K key) {
        Map v = map.get(key);

        if (v == null) {
            v = new HashMap<>();
            v = map.put(key, v);
        }

        return v;
    }

    /**
     * Check if the specified Map contains the specified Entry
     * 
     * @param map
     * @param entry
     * @return
     */
    public static boolean contains(final Map map, final Map.Entry entry) {
        return contains(map, entry.getKey(), entry.getValue());
    }

    public static boolean contains(final Map map, final Object key, final Object value) {
        if (N.isNullOrEmpty(map)) {
            return false;
        }

        final Object val = map.get(key);

        return val == null ? value == null && map.containsKey(key) : N.equals(val, value);
    }

    public static  Map intersection(final Map map, final Map map2) {
        if (N.isNullOrEmpty(map) || N.isNullOrEmpty(map2)) {
            return new LinkedHashMap<>();
        }

        final Map result = map instanceof IdentityHashMap ? new IdentityHashMap() : new LinkedHashMap();
        Object val = null;

        for (Map.Entry entry : map.entrySet()) {
            val = map2.get(entry.getKey());

            if ((val != null && N.equals(val, entry.getValue())) || (entry.getValue() == null && map.containsKey(entry.getKey()))) {
                result.put(entry.getKey(), entry.getValue());
            }
        }

        return result;
    }

    public static  Map>> difference(final Map map, final Map map2) {
        if (N.isNullOrEmpty(map)) {
            return new LinkedHashMap<>();
        }

        final Map>> result = map instanceof IdentityHashMap ? new IdentityHashMap>>()
                : new LinkedHashMap>>();

        if (N.isNullOrEmpty(map2)) {
            for (Map.Entry entry : map.entrySet()) {
                result.put(entry.getKey(), Pair.of(entry.getValue(), Nullable. empty()));
            }
        } else {
            V val = null;

            for (Map.Entry entry : map.entrySet()) {
                val = map2.get(entry.getKey());

                if (val == null && map2.containsKey(entry.getKey()) == false) {
                    result.put(entry.getKey(), Pair.of(entry.getValue(), Nullable. empty()));
                } else if (N.equals(val, entry.getValue()) == false) {
                    result.put(entry.getKey(), Pair.of(entry.getValue(), Nullable.of(val)));
                }
            }
        }

        return result;
    }

    public static  Map, Nullable>> symmetricDifference(final Map map, final Map map2) {
        final boolean isIdentityHashMap = (N.notNullOrEmpty(map) && map instanceof IdentityHashMap)
                || (N.notNullOrEmpty(map2) && map2 instanceof IdentityHashMap);

        final Map, Nullable>> result = isIdentityHashMap ? new IdentityHashMap, Nullable>>()
                : new LinkedHashMap, Nullable>>();

        if (N.notNullOrEmpty(map)) {
            if (N.isNullOrEmpty(map2)) {
                for (Map.Entry entry : map.entrySet()) {
                    result.put(entry.getKey(), Pair.of(Nullable.of(entry.getValue()), Nullable. empty()));
                }
            } else {
                K key = null;
                V val2 = null;

                for (Map.Entry entry : map.entrySet()) {
                    key = entry.getKey();
                    val2 = map2.get(key);

                    if (val2 == null && map2.containsKey(key) == false) {
                        result.put(key, Pair.of(Nullable.of(entry.getValue()), Nullable. empty()));
                    } else if (N.equals(val2, entry.getValue()) == false) {
                        result.put(key, Pair.of(Nullable.of(entry.getValue()), Nullable.of(val2)));
                    }
                }
            }
        }

        if (N.notNullOrEmpty(map2)) {
            if (N.isNullOrEmpty(map)) {
                for (Map.Entry entry : map2.entrySet()) {
                    result.put(entry.getKey(), Pair.of(Nullable. empty(), Nullable.of(entry.getValue())));
                }
            } else {
                for (Map.Entry entry : map2.entrySet()) {
                    if (map.containsKey(entry.getKey()) == false) {
                        result.put(entry.getKey(), Pair.of(Nullable. empty(), Nullable.of(entry.getValue())));
                    }
                }
            }
        }

        return result;
    }

    public static  V putIfAbsent(final Map map, K key, final V value) {
        V v = map.get(key);

        if (v == null) {
            v = map.put(key, value);
        }

        return v;
    }

    public static  V putIfAbsent(final Map map, K key, final Supplier supplier) {
        V v = map.get(key);

        if (v == null) {
            v = map.put(key, supplier.get());
        }

        return v;
    }

    /**
     * Removes the specified entry.
     * 
     * @param map
     * @param entry
     * @return
     */
    public static  boolean remove(final Map map, Map.Entry entry) {
        return remove(map, entry.getKey(), entry.getValue());
    }

    public static  boolean remove(final Map map, final Object key, final Object value) {
        if (N.isNullOrEmpty(map)) {
            return false;
        }

        final Object curValue = map.get(key);

        if (!N.equals(curValue, value) || (curValue == null && !map.containsKey(key))) {
            return false;
        }

        map.remove(key);
        return true;
    }

    /**
     * 
     * @param map
     * @param keysToRemove
     * @return true if any key/value was removed, otherwise false.
     */
    public static boolean removeKeys(final Map map, final Collection keysToRemove) {
        if (N.isNullOrEmpty(map) || N.isNullOrEmpty(keysToRemove)) {
            return false;
        }

        final int originalSize = map.size();

        for (Object key : keysToRemove) {
            map.remove(key);
        }

        return map.size() < originalSize;
    }

    /**
     * The the entries from the specified Map
     * 
     * @param map
     * @param entriesToRemove
     * @return true if any key/value was removed, otherwise false.
     */
    public static boolean removeEntries(final Map map, final Map entriesToRemove) {
        if (N.isNullOrEmpty(map) || N.isNullOrEmpty(entriesToRemove)) {
            return false;
        }

        final int originalSize = map.size();

        for (Map.Entry entry : entriesToRemove.entrySet()) {
            if (N.equals(map.get(entry.getKey()), entry.getValue())) {
                map.remove(entry.getKey());
            }
        }

        return map.size() < originalSize;
    }

    /**
     * Removes entries from the specified {@code map} by the the specified {@code filter}.
     * 
     * @param map
     * @param filter
     * @return {@code true} if there are one or more than one entries removed from the specified map.
     * @throws E
     */
    public static  boolean removeIf(final Map map, final Try.Predicate, E> filter) throws E {
        List keysToRemove = null;

        for (Map.Entry entry : map.entrySet()) {
            if (filter.test(entry)) {
                if (keysToRemove == null) {
                    keysToRemove = new ArrayList<>(7);
                }

                keysToRemove.add(entry.getKey());
            }
        }

        if (N.notNullOrEmpty(keysToRemove)) {
            for (K key : keysToRemove) {
                map.remove(key);
            }

            return true;
        }

        return false;
    }

    /**
     * Removes entries from the specified {@code map} by the the specified {@code filter}.
     * 
     * @param map
     * @param filter
     * @return {@code true} if there are one or more than one entries removed from the specified map.
     * @throws E
     */
    public static  boolean removeIfKey(final Map map, final Try.Predicate filter) throws E {
        List keysToRemove = null;

        for (Map.Entry entry : map.entrySet()) {
            if (filter.test(entry.getKey())) {
                if (keysToRemove == null) {
                    keysToRemove = new ArrayList<>(7);
                }

                keysToRemove.add(entry.getKey());
            }
        }

        if (N.notNullOrEmpty(keysToRemove)) {
            for (K key : keysToRemove) {
                map.remove(key);
            }

            return true;
        }

        return false;
    }

    /**
     * Removes entries from the specified {@code map} by the the specified {@code filter}.
     * 
     * @param map
     * @param filter
     * @return {@code true} if there are one or more than one entries removed from the specified map.
     * @throws E
     */
    public static  boolean removeIfValue(final Map map, final Try.Predicate filter) throws E {
        List keysToRemove = null;

        for (Map.Entry entry : map.entrySet()) {
            if (filter.test(entry.getValue())) {
                if (keysToRemove == null) {
                    keysToRemove = new ArrayList<>(7);
                }

                keysToRemove.add(entry.getKey());
            }
        }

        if (N.notNullOrEmpty(keysToRemove)) {
            for (K key : keysToRemove) {
                map.remove(key);
            }

            return true;
        }

        return false;
    }

    public static  boolean replace(final Map map, final K key, final V oldValue, final V newValue) {
        if (N.isNullOrEmpty(map)) {
            return false;
        }

        final Object curValue = map.get(key);

        if (!N.equals(curValue, oldValue) || (curValue == null && !map.containsKey(key))) {
            return false;
        }

        map.put(key, newValue);
        return true;
    }

    public static  V replace(final Map map, final K key, final V newValue) {
        if (N.isNullOrEmpty(map)) {
            return null;
        }

        V curValue = null;

        if (((curValue = map.get(key)) != null) || map.containsKey(key)) {
            curValue = map.put(key, newValue);
        }

        return curValue;
    }

    public static  void replaceAll(final Map map, final Try.BiFunction function)
            throws E {
        N.checkArgNotNull(function);

        if (N.isNullOrEmpty(map)) {
            return;
        }

        K k = null;
        V v = null;

        for (Map.Entry entry : map.entrySet()) {
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch (IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }

            // ise thrown from function is not a cme.
            v = function.apply(k, v);

            try {
                entry.setValue(v);
            } catch (IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
        }
    }

    public static  void forEach(final Map map, final Try.BiConsumer action) throws E {
        N.checkArgNotNull(action);

        if (N.isNullOrEmpty(map)) {
            return;
        }

        for (Map.Entry entry : map.entrySet()) {
            action.accept(entry.getKey(), entry.getValue());
        }
    }

    public static  Map filter(final Map map, final Try.BiPredicate predicate) throws E {
        if (map == null) {
            return new HashMap();
        }

        final Map result = newTargetMap(map, 0);

        for (Map.Entry entry : map.entrySet()) {
            if (predicate.test(entry.getKey(), entry.getValue())) {
                result.put(entry.getKey(), entry.getValue());
            }
        }

        return result;
    }

    public static  Map filterByKey(final Map map, final Try.Predicate predicate) throws E {
        if (map == null) {
            return new HashMap();
        }

        final Map result = newTargetMap(map, 0);

        for (Map.Entry entry : map.entrySet()) {
            if (predicate.test(entry.getKey())) {
                result.put(entry.getKey(), entry.getValue());
            }
        }

        return result;
    }

    public static  Map filterByValue(final Map map, final Try.Predicate predicate) throws E {
        if (map == null) {
            return new HashMap();
        }

        final Map result = newTargetMap(map, 0);

        for (Map.Entry entry : map.entrySet()) {
            if (predicate.test(entry.getValue())) {
                result.put(entry.getKey(), entry.getValue());
            }
        }

        return result;
    }

    /**
     * 
     * @param map
     * @return
     * @see Multimap#invertFrom(Map, com.landawn.abacus.util.function.Supplier)
     * @see ListMultimap#invertFrom(Map)
     * @see ListMultimap#invertFrom(Map)
     */
    public static  Map invert(final Map map) {
        if (map == null) {
            return new HashMap();
        }

        final Map result = newOrderingMap(map);

        for (Map.Entry entry : map.entrySet()) {
            result.put(entry.getValue(), entry.getKey());
        }

        return result;
    }

    public static  Map invert(final Map map, Try.BinaryOperator mergeOp) throws E {
        N.checkArgNotNull(mergeOp, "mergeOp");

        if (map == null) {
            return new HashMap();
        }

        final Map result = newOrderingMap(map);
        K oldVal = null;

        for (Map.Entry entry : map.entrySet()) {
            oldVal = result.get(entry.getValue());

            if (oldVal != null || result.containsKey(entry.getValue())) {
                result.put(entry.getValue(), mergeOp.apply(oldVal, entry.getKey()));
            } else {
                result.put(entry.getValue(), entry.getKey());
            }
        }

        return result;
    }

    /**
     * 
     * @param map
     * @return
     * @see Multimap#flatInvertFrom(Map, com.landawn.abacus.util.function.Supplier)
     * @see ListMultimap#flatInvertFrom(Map)
     * @see SetMultimap#flatInvertFrom(Map)
     */
    public static  Map> flatInvert(final Map> map) {
        if (map == null) {
            return new HashMap>();
        }

        final Map> result = newOrderingMap(map);

        for (Map.Entry> entry : map.entrySet()) {
            final Collection c = entry.getValue();

            if (N.notNullOrEmpty(c)) {
                for (V v : c) {
                    List list = result.get(v);

                    if (list == null) {
                        list = new ArrayList<>();
                        result.put(v, list);
                    }

                    list.add(entry.getKey());
                }
            }
        }

        return result;
    }

    public static  T map2Entity(final Class targetClass, final Map m) {
        return map2Entity(targetClass, m, false, true);
    }

    @SuppressWarnings("unchecked")
    public static  T map2Entity(final Class targetClass, final Map m, final boolean ignoreNullProperty,
            final boolean ignoreUnknownProperty) {
        checkEntityClass(targetClass);

        final T entity = N.newInstance(targetClass);

        String propName = null;
        Object propValue = null;
        Method propSetMethod = null;
        Class paramClass = null;

        for (Map.Entry entry : m.entrySet()) {
            propName = entry.getKey();
            propValue = entry.getValue();

            if (ignoreNullProperty && (propValue == null)) {
                continue;
            }

            propSetMethod = ClassUtil.getPropSetMethod(targetClass, propName);

            if (propSetMethod == null) {
                ClassUtil.setPropValue(entity, propName, propValue, ignoreUnknownProperty);
            } else {
                paramClass = propSetMethod.getParameterTypes()[0];

                if (propValue != null && N.typeOf(propValue.getClass()).isMap() && N.isEntity(paramClass)) {
                    ClassUtil.setPropValue(entity, propSetMethod,
                            map2Entity(paramClass, (Map) propValue, ignoreNullProperty, ignoreUnknownProperty));
                } else {
                    ClassUtil.setPropValue(entity, propSetMethod, propValue);
                }
            }
        }

        return entity;
    }

    public static  T map2Entity(final Class targetClass, final Map m, final Collection selectPropNames) {
        checkEntityClass(targetClass);

        final T entity = N.newInstance(targetClass);

        Object propValue = null;
        Method propSetMethod = null;
        Class paramClass = null;

        for (String propName : selectPropNames) {
            propValue = m.get(propName);

            if (propValue == null && m.containsKey(propName) == false) {
                throw new IllegalArgumentException("Property name: " + propName + " is not found in map with key set: " + m.keySet());
            }

            propSetMethod = ClassUtil.getPropSetMethod(targetClass, propName);

            if (propSetMethod == null) {
                ClassUtil.setPropValue(entity, propName, propValue, false);
            } else {
                paramClass = propSetMethod.getParameterTypes()[0];

                if (propValue != null && N.typeOf(propValue.getClass()).isMap() && N.isEntity(paramClass)) {
                    ClassUtil.setPropValue(entity, propSetMethod, map2Entity(paramClass, (Map) propValue));
                } else {
                    ClassUtil.setPropValue(entity, propSetMethod, propValue);
                }
            }
        }

        return entity;
    }

    public static  List map2Entity(final Class targetClass, final Collection> mList) {
        return map2Entity(targetClass, mList, false, true);
    }

    public static  List map2Entity(final Class targetClass, final Collection> mList, final boolean igoreNullProperty,
            final boolean ignoreUnknownProperty) {
        checkEntityClass(targetClass);

        final List entityList = new ArrayList<>(mList.size());

        for (Map m : mList) {
            entityList.add(map2Entity(targetClass, m, igoreNullProperty, ignoreUnknownProperty));
        }

        return entityList;
    }

    public static  List map2Entity(final Class targetClass, final Collection> mList, final Collection selectPropNames) {
        checkEntityClass(targetClass);

        final List entityList = new ArrayList<>(mList.size());

        for (Map m : mList) {
            entityList.add(map2Entity(targetClass, m, selectPropNames));
        }

        return entityList;
    }

    private static  void checkEntityClass(final Class cls) {
        if (!N.isEntity(cls)) {
            throw new IllegalArgumentException("No property getter/setter method is found in the specified class: " + ClassUtil.getCanonicalClassName(cls));
        }
    }

    public static Map entity2Map(final Object entity) {
        return entity2Map(entity, false);
    }

    public static Map entity2Map(final Object entity, final boolean ignoreNullProperty) {
        return entity2Map(entity, ignoreNullProperty, null);
    }

    public static Map entity2Map(final Object entity, final Collection ignoredPropNames) {
        return entity2Map(entity, false, ignoredPropNames);
    }

    public static Map entity2Map(final Object entity, final boolean ignoreNullProperty, final Collection ignoredPropNames) {
        return entity2Map(entity, ignoreNullProperty, ignoredPropNames, NamingPolicy.LOWER_CAMEL_CASE);
    }

    @SuppressWarnings("deprecation")
    public static Map entity2Map(final Object entity, final boolean ignoreNullProperty, final Collection ignoredPropNames,
            final NamingPolicy keyNamingPolicy) {
        final int initCapacity = (entity instanceof DirtyMarker ? ((DirtyMarker) entity).signedPropNames().size()
                : N.initHashCapacity(ClassUtil.getPropGetMethodList(entity.getClass()).size()));
        final Map resultMap = new LinkedHashMap<>(initCapacity);

        entity2Map(resultMap, entity, ignoreNullProperty, ignoredPropNames, keyNamingPolicy);

        return resultMap;
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @return the input resultMap
     */
    public static > M entity2Map(final Object entity, final Supplier mapSupplier) {
        return entity2Map(mapSupplier.get(), entity);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @return the input resultMap
     */
    public static > M entity2Map(final M resultMap, final Object entity) {
        return entity2Map(resultMap, entity, false);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @param ignoreNullProperty
     * @return the input resultMap
     */
    public static > M entity2Map(final M resultMap, final Object entity, final boolean ignoreNullProperty) {
        return entity2Map(resultMap, entity, ignoreNullProperty, null);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @param ignoredPropNames
     * @return the input resultMap
     */
    public static > M entity2Map(final M resultMap, final Object entity, final Collection ignoredPropNames) {
        return entity2Map(resultMap, entity, false, ignoredPropNames);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @param ignoreNullProperty
     * @param ignoredPropNames
     * @return the input resultMap
     */
    public static > M entity2Map(final M resultMap, final Object entity, final boolean ignoreNullProperty,
            final Collection ignoredPropNames) {
        return entity2Map(resultMap, entity, ignoreNullProperty, ignoredPropNames, NamingPolicy.LOWER_CAMEL_CASE);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @param ignoreNullProperty
     * @param ignoredPropNames
     * @param keyNamingPolicy
     * @return the input resultMap
     */
    @SuppressWarnings("deprecation")
    public static > M entity2Map(final M resultMap, final Object entity, final boolean ignoreNullProperty,
            final Collection ignoredPropNames, NamingPolicy keyNamingPolicy) {
        keyNamingPolicy = keyNamingPolicy == null ? NamingPolicy.LOWER_CAMEL_CASE : keyNamingPolicy;

        final boolean hasIgnoredPropNames = N.notNullOrEmpty(ignoredPropNames);
        Set signedPropNames = null;

        if (entity instanceof DirtyMarker) {
            final Class entityClass = entity.getClass();
            signedPropNames = ((DirtyMarker) entity).signedPropNames();

            if (signedPropNames.size() == 0) {
                // logger.warn("no property is signed in the specified source entity: "
                // + toString(entity));

                return resultMap;
            } else {
                final Set tmp = new HashSet<>(N.initHashCapacity(signedPropNames.size()));

                for (String propName : signedPropNames) {
                    tmp.add(ClassUtil.getPropNameByMethod(ClassUtil.getPropGetMethod(entityClass, propName)));
                }

                signedPropNames = tmp;
            }
        }

        final Map getterMethodList = ClassUtil.checkPropGetMethodList(entity.getClass());
        String propName = null;
        Object propValue = null;

        try {
            switch (keyNamingPolicy) {
                case LOWER_CAMEL_CASE: {
                    for (Map.Entry entry : getterMethodList.entrySet()) {
                        propName = entry.getKey();

                        if (signedPropNames != null && signedPropNames.contains(propName) == false) {
                            continue;
                        }

                        if (hasIgnoredPropNames && ignoredPropNames.contains(propName)) {
                            continue;
                        }

                        propValue = entry.getValue().invoke(entity);

                        if (ignoreNullProperty && (propValue == null)) {
                            continue;
                        }

                        resultMap.put(propName, propValue);
                    }

                    break;
                }

                case LOWER_CASE_WITH_UNDERSCORE: {
                    for (Map.Entry entry : getterMethodList.entrySet()) {
                        propName = entry.getKey();

                        if (signedPropNames != null && signedPropNames.contains(propName) == false) {
                            continue;
                        }

                        if (hasIgnoredPropNames && ignoredPropNames.contains(propName)) {
                            continue;
                        }

                        propValue = entry.getValue().invoke(entity);

                        if (ignoreNullProperty && (propValue == null)) {
                            continue;
                        }

                        resultMap.put(ClassUtil.toLowerCaseWithUnderscore(propName), propValue);
                    }

                    break;
                }

                case UPPER_CASE_WITH_UNDERSCORE: {
                    for (Map.Entry entry : getterMethodList.entrySet()) {
                        propName = entry.getKey();

                        if (signedPropNames != null && signedPropNames.contains(propName) == false) {
                            continue;
                        }

                        if (hasIgnoredPropNames && ignoredPropNames.contains(propName)) {
                            continue;
                        }

                        propValue = entry.getValue().invoke(entity);

                        if (ignoreNullProperty && (propValue == null)) {
                            continue;
                        }

                        resultMap.put(ClassUtil.toUpperCaseWithUnderscore(propName), propValue);
                    }

                    break;
                }

                default:
                    throw new IllegalArgumentException("Unsupported NamingPolicy: " + keyNamingPolicy);
            }
        } catch (Exception e) {
            throw N.toRuntimeException(e);
        }

        return resultMap;
    }

    public static List> entity2Map(final Collection entityList) {
        return entity2Map(entityList, false);
    }

    public static List> entity2Map(final Collection entityList, final boolean ignoreNullProperty) {
        return entity2Map(entityList, ignoreNullProperty, null);
    }

    public static List> entity2Map(final Collection entityList, final Collection ignoredPropNames) {
        return entity2Map(entityList, false, ignoredPropNames);
    }

    public static List> entity2Map(final Collection entityList, final boolean ignoreNullProperty,
            final Collection ignoredPropNames) {
        return entity2Map(entityList, ignoreNullProperty, ignoredPropNames, NamingPolicy.LOWER_CAMEL_CASE);
    }

    public static List> entity2Map(final Collection entityList, final boolean ignoreNullProperty,
            final Collection ignoredPropNames, final NamingPolicy keyNamingPolicy) {
        final List> resultList = new ArrayList<>(entityList.size());

        for (Object entity : entityList) {
            resultList.add(entity2Map(entity, ignoreNullProperty, ignoredPropNames, keyNamingPolicy));
        }

        return resultList;
    }

    public static Map deepEntity2Map(final Object entity) {
        return deepEntity2Map(entity, false);
    }

    public static Map deepEntity2Map(final Object entity, final boolean ignoreNullProperty) {
        return deepEntity2Map(entity, ignoreNullProperty, null);
    }

    public static Map deepEntity2Map(final Object entity, final Collection ignoredPropNames) {
        return deepEntity2Map(entity, false, ignoredPropNames);
    }

    public static Map deepEntity2Map(final Object entity, final boolean ignoreNullProperty, final Collection ignoredPropNames) {
        return deepEntity2Map(entity, ignoreNullProperty, ignoredPropNames, NamingPolicy.LOWER_CAMEL_CASE);
    }

    @SuppressWarnings("deprecation")
    public static Map deepEntity2Map(final Object entity, final boolean ignoreNullProperty, final Collection ignoredPropNames,
            final NamingPolicy keyNamingPolicy) {
        final int initCapacity = entity instanceof DirtyMarker ? ((DirtyMarker) entity).signedPropNames().size()
                : N.initHashCapacity(ClassUtil.getPropGetMethodList(entity.getClass()).size());
        final Map resultMap = new LinkedHashMap<>(initCapacity);

        deepEntity2Map(resultMap, entity, ignoreNullProperty, ignoredPropNames, keyNamingPolicy);

        return resultMap;
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @return the input resultMap
     */
    public static > M deepEntity2Map(final Object entity, final Supplier mapSupplier) {
        return deepEntity2Map(mapSupplier.get(), entity);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @return the input resultMap
     */
    public static > M deepEntity2Map(final M resultMap, final Object entity) {
        return deepEntity2Map(resultMap, entity, false);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @param ignoreNullProperty
     * @return the input resultMap
     */
    public static > M deepEntity2Map(final M resultMap, final Object entity, final boolean ignoreNullProperty) {
        return deepEntity2Map(resultMap, entity, ignoreNullProperty, null);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @param ignoredPropNames
     * @return the input resultMap
     */
    public static > M deepEntity2Map(final M resultMap, final Object entity, final Collection ignoredPropNames) {
        return deepEntity2Map(resultMap, entity, false, ignoredPropNames);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @param ignoreNullProperty
     * @param ignoredPropNames
     * @return the input resultMap
     */
    public static > M deepEntity2Map(final M resultMap, final Object entity, final boolean ignoreNullProperty,
            final Collection ignoredPropNames) {
        return deepEntity2Map(resultMap, entity, ignoreNullProperty, ignoredPropNames, NamingPolicy.LOWER_CAMEL_CASE);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @param ignoreNullProperty
     * @param ignoredPropNames
     * @param keyNamingPolicy
     * @return the input resultMap
     */
    @SuppressWarnings("deprecation")
    public static > M deepEntity2Map(final M resultMap, final Object entity, final boolean ignoreNullProperty,
            final Collection ignoredPropNames, NamingPolicy keyNamingPolicy) {
        keyNamingPolicy = keyNamingPolicy == null ? NamingPolicy.LOWER_CAMEL_CASE : keyNamingPolicy;
        final boolean hasIgnoredPropNames = N.notNullOrEmpty(ignoredPropNames);

        Set signedPropNames = null;

        if (entity instanceof DirtyMarker) {
            final Class entityClass = entity.getClass();
            signedPropNames = ((DirtyMarker) entity).signedPropNames();

            if (signedPropNames.size() == 0) {
                // logger.warn("no property is signed in the specified source entity: "
                // + toString(entity));

                return resultMap;
            } else {
                final Set tmp = new HashSet<>(N.initHashCapacity(signedPropNames.size()));

                for (String propName : signedPropNames) {
                    tmp.add(ClassUtil.getPropNameByMethod(ClassUtil.getPropGetMethod(entityClass, propName)));
                }

                signedPropNames = tmp;
            }
        }

        final Map getterMethodList = ClassUtil.checkPropGetMethodList(entity.getClass());
        String propName = null;
        Object propValue = null;

        try {
            switch (keyNamingPolicy) {
                case LOWER_CAMEL_CASE: {
                    for (Map.Entry entry : getterMethodList.entrySet()) {
                        propName = entry.getKey();

                        if (signedPropNames != null && signedPropNames.contains(propName) == false) {
                            continue;
                        }

                        if (hasIgnoredPropNames && ignoredPropNames.contains(propName)) {
                            continue;
                        }

                        propValue = entry.getValue().invoke(entity);

                        if (ignoreNullProperty && (propValue == null)) {
                            continue;
                        }

                        if ((propValue == null) || !N.isEntity(propValue.getClass())) {
                            resultMap.put(propName, propValue);
                        } else {
                            resultMap.put(propName, deepEntity2Map(propValue, ignoreNullProperty, null, keyNamingPolicy));
                        }
                    }

                    break;
                }

                case LOWER_CASE_WITH_UNDERSCORE: {
                    for (Map.Entry entry : getterMethodList.entrySet()) {
                        propName = entry.getKey();

                        if (signedPropNames != null && signedPropNames.contains(propName) == false) {
                            continue;
                        }

                        if (hasIgnoredPropNames && ignoredPropNames.contains(propName)) {
                            continue;
                        }

                        propValue = entry.getValue().invoke(entity);

                        if (ignoreNullProperty && (propValue == null)) {
                            continue;
                        }

                        if ((propValue == null) || !N.isEntity(propValue.getClass())) {
                            resultMap.put(ClassUtil.toLowerCaseWithUnderscore(propName), propValue);
                        } else {
                            resultMap.put(ClassUtil.toLowerCaseWithUnderscore(propName), deepEntity2Map(propValue, ignoreNullProperty, null, keyNamingPolicy));
                        }
                    }

                    break;
                }

                case UPPER_CASE_WITH_UNDERSCORE: {
                    for (Map.Entry entry : getterMethodList.entrySet()) {
                        propName = entry.getKey();

                        if (signedPropNames != null && signedPropNames.contains(propName) == false) {
                            continue;
                        }

                        if (hasIgnoredPropNames && ignoredPropNames.contains(propName)) {
                            continue;
                        }

                        propValue = entry.getValue().invoke(entity);

                        if (ignoreNullProperty && (propValue == null)) {
                            continue;
                        }

                        if ((propValue == null) || !N.isEntity(propValue.getClass())) {
                            resultMap.put(ClassUtil.toUpperCaseWithUnderscore(propName), propValue);
                        } else {
                            resultMap.put(ClassUtil.toUpperCaseWithUnderscore(propName), deepEntity2Map(propValue, ignoreNullProperty, null, keyNamingPolicy));
                        }
                    }

                    break;
                }

                default:
                    throw new IllegalArgumentException("Unsupported NamingPolicy: " + keyNamingPolicy);
            }
        } catch (Exception e) {
            throw N.toRuntimeException(e);
        }

        return resultMap;
    }

    public static List> deepEntity2Map(final Collection entityList) {
        return deepEntity2Map(entityList, false);
    }

    public static List> deepEntity2Map(final Collection entityList, final boolean ignoreNullProperty) {
        return deepEntity2Map(entityList, ignoreNullProperty, null);
    }

    public static List> deepEntity2Map(final Collection entityList, final Collection ignoredPropNames) {
        final boolean ignoreNullProperty = N.isNullOrEmpty(entityList) ? true
                : (entityList instanceof ArrayList ? ((ArrayList) entityList).get(0) : entityList.iterator().next()) instanceof DirtyMarker == false;

        return deepEntity2Map(entityList, ignoreNullProperty, ignoredPropNames);
    }

    public static List> deepEntity2Map(final Collection entityList, final boolean ignoreNullProperty,
            final Collection ignoredPropNames) {
        return deepEntity2Map(entityList, ignoreNullProperty, ignoredPropNames, NamingPolicy.LOWER_CAMEL_CASE);
    }

    public static List> deepEntity2Map(final Collection entityList, final boolean ignoreNullProperty,
            final Collection ignoredPropNames, final NamingPolicy keyNamingPolicy) {
        final List> resultList = new ArrayList<>(entityList.size());

        for (Object entity : entityList) {
            resultList.add(deepEntity2Map(entity, ignoreNullProperty, ignoredPropNames, keyNamingPolicy));
        }

        return resultList;
    }

    public static Map entity2FlatMap(final Object entity) {
        return entity2FlatMap(entity, false);
    }

    public static Map entity2FlatMap(final Object entity, final boolean ignoreNullProperty) {
        return entity2FlatMap(entity, ignoreNullProperty, null);
    }

    public static Map entity2FlatMap(final Object entity, final Collection ignoredPropNames) {
        return entity2FlatMap(entity, false, ignoredPropNames);
    }

    public static Map entity2FlatMap(final Object entity, final boolean ignoreNullProperty, final Collection ignoredPropNames) {
        return entity2FlatMap(entity, ignoreNullProperty, ignoredPropNames, NamingPolicy.LOWER_CAMEL_CASE);
    }

    @SuppressWarnings("deprecation")
    public static Map entity2FlatMap(final Object entity, final boolean ignoreNullProperty, final Collection ignoredPropNames,
            final NamingPolicy keyNamingPolicy) {
        final int initCapacity = entity instanceof DirtyMarker ? ((DirtyMarker) entity).signedPropNames().size()
                : N.initHashCapacity(ClassUtil.getPropGetMethodList(entity.getClass()).size());
        final Map resultMap = new LinkedHashMap<>(initCapacity);

        entity2FlatMap(resultMap, entity, ignoreNullProperty, ignoredPropNames, keyNamingPolicy);

        return resultMap;
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @return the input resultMap
     */
    public static > M entity2FlatMap(final Object entity, final Supplier mapSupplier) {
        return entity2FlatMap(mapSupplier.get(), entity);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @return the input resultMap
     */
    public static > M entity2FlatMap(final M resultMap, final Object entity) {
        return entity2FlatMap(resultMap, entity, false);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @param ignoreNullProperty
     * @return the input resultMap
     */
    public static > M entity2FlatMap(final M resultMap, final Object entity, final boolean ignoreNullProperty) {
        return entity2FlatMap(resultMap, entity, ignoreNullProperty, null);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @param ignoredPropNames
     * @return the input resultMap
     */
    public static > M entity2FlatMap(final M resultMap, final Object entity, final Collection ignoredPropNames) {
        return entity2FlatMap(resultMap, entity, false, ignoredPropNames);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @param ignoreNullProperty
     * @param ignoredPropNames
     * @return the input resultMap
     */
    public static > M entity2FlatMap(final M resultMap, final Object entity, final boolean ignoreNullProperty,
            final Collection ignoredPropNames) {
        return entity2FlatMap(resultMap, entity, ignoreNullProperty, ignoredPropNames, NamingPolicy.LOWER_CAMEL_CASE);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @param ignoreNullProperty
     * @param ignoredPropNames
     * @param keyNamingPolicy
     * @return the input resultMap
     */
    public static > M entity2FlatMap(final M resultMap, final Object entity, final boolean ignoreNullProperty,
            final Collection ignoredPropNames, final NamingPolicy keyNamingPolicy) {
        return entity2FlatMap(resultMap, entity, ignoreNullProperty, ignoredPropNames, keyNamingPolicy, null);
    }

    /**
     *
     * @param resultMap
     * @param entity
     * @param ignoreNullProperty
     * @param ignoredPropNames
     * @param keyNamingPolicy
     * @param parentPropName
     * @return the input resultMap
     */
    @SuppressWarnings("deprecation")
    static > T entity2FlatMap(final T resultMap, final Object entity, final boolean ignoreNullProperty,
            final Collection ignoredPropNames, final NamingPolicy keyNamingPolicy, final String parentPropName) {
        final boolean hasIgnoredPropNames = N.notNullOrEmpty(ignoredPropNames);
        final boolean isNullParentPropName = (parentPropName == null);

        if (entity instanceof DirtyMarker) {
            final Class entityClass = entity.getClass();
            final Set signedPropNames = ((DirtyMarker) entity).signedPropNames();

            if (signedPropNames.size() == 0) {
                // logger.warn("no property is signed in the specified source entity: "
                // + toString(entity));
            } else {
                Method propGetMethod = null;
                Object propValue = null;

                try {
                    switch (keyNamingPolicy) {
                        case LOWER_CAMEL_CASE: {
                            for (String propName : signedPropNames) {
                                propGetMethod = ClassUtil.getPropGetMethod(entityClass, propName);
                                propName = ClassUtil.getPropNameByMethod(propGetMethod);

                                if (hasIgnoredPropNames && ignoredPropNames.contains(propName)) {
                                    continue;
                                }

                                propValue = propGetMethod.invoke(entity);

                                if (ignoreNullProperty && (propValue == null)) {
                                    continue;
                                }

                                if ((propValue == null) || !N.isEntity(propValue.getClass())) {
                                    if (isNullParentPropName) {
                                        resultMap.put(propName, propValue);
                                    } else {
                                        resultMap.put(parentPropName + WD.PERIOD + propName, propValue);
                                    }
                                } else {
                                    if (isNullParentPropName) {
                                        entity2FlatMap(resultMap, propValue, ignoreNullProperty, null, keyNamingPolicy, propName);
                                    } else {
                                        entity2FlatMap(resultMap, propValue, ignoreNullProperty, null, keyNamingPolicy, parentPropName + WD.PERIOD + propName);
                                    }
                                }
                            }

                            break;
                        }

                        case LOWER_CASE_WITH_UNDERSCORE: {
                            for (String propName : signedPropNames) {
                                propGetMethod = ClassUtil.getPropGetMethod(entityClass, propName);
                                propName = ClassUtil.getPropNameByMethod(propGetMethod);

                                if (hasIgnoredPropNames && ignoredPropNames.contains(propName)) {
                                    continue;
                                }

                                propName = ClassUtil.toLowerCaseWithUnderscore(propName);
                                propValue = propGetMethod.invoke(entity);

                                if (ignoreNullProperty && (propValue == null)) {
                                    continue;
                                }

                                if ((propValue == null) || !N.isEntity(propValue.getClass())) {
                                    if (isNullParentPropName) {
                                        resultMap.put(propName, propValue);
                                    } else {
                                        resultMap.put(parentPropName + WD.PERIOD + propName, propValue);
                                    }
                                } else {
                                    if (isNullParentPropName) {
                                        entity2FlatMap(resultMap, propValue, ignoreNullProperty, null, keyNamingPolicy, propName);
                                    } else {
                                        entity2FlatMap(resultMap, propValue, ignoreNullProperty, null, keyNamingPolicy, parentPropName + WD.PERIOD + propName);
                                    }
                                }
                            }

                            break;
                        }

                        case UPPER_CASE_WITH_UNDERSCORE: {
                            for (String propName : signedPropNames) {
                                propGetMethod = ClassUtil.getPropGetMethod(entityClass, propName);
                                propName = ClassUtil.getPropNameByMethod(propGetMethod);

                                if (hasIgnoredPropNames && ignoredPropNames.contains(propName)) {
                                    continue;
                                }

                                propName = ClassUtil.toUpperCaseWithUnderscore(propName);
                                propValue = propGetMethod.invoke(entity);

                                if (ignoreNullProperty && (propValue == null)) {
                                    continue;
                                }

                                if ((propValue == null) || !N.isEntity(propValue.getClass())) {
                                    if (isNullParentPropName) {
                                        resultMap.put(propName, propValue);
                                    } else {
                                        resultMap.put(parentPropName + WD.PERIOD + propName, propValue);
                                    }
                                } else {
                                    if (isNullParentPropName) {
                                        entity2FlatMap(resultMap, propValue, ignoreNullProperty, null, keyNamingPolicy, propName);
                                    } else {
                                        entity2FlatMap(resultMap, propValue, ignoreNullProperty, null, keyNamingPolicy, parentPropName + WD.PERIOD + propName);
                                    }
                                }
                            }

                            break;
                        }

                        default:
                            throw new IllegalArgumentException("Unsupported NamingPolicy: " + keyNamingPolicy);
                    }

                } catch (Exception e) {
                    throw N.toRuntimeException(e);
                }
            }
        } else {
            final Map getterMethodList = ClassUtil.checkPropGetMethodList(entity.getClass());
            String propName = null;
            Object propValue = null;

            try {
                switch (keyNamingPolicy) {
                    case LOWER_CAMEL_CASE: {
                        for (Map.Entry entry : getterMethodList.entrySet()) {
                            propName = entry.getKey();

                            if (hasIgnoredPropNames && ignoredPropNames.contains(propName)) {
                                continue;
                            }

                            propValue = entry.getValue().invoke(entity);

                            if (ignoreNullProperty && (propValue == null)) {
                                continue;
                            }

                            if ((propValue == null) || !N.isEntity(propValue.getClass())) {
                                if (isNullParentPropName) {
                                    resultMap.put(propName, propValue);
                                } else {
                                    resultMap.put(parentPropName + WD.PERIOD + propName, propValue);
                                }
                            } else {
                                if (isNullParentPropName) {
                                    entity2FlatMap(resultMap, propValue, ignoreNullProperty, null, keyNamingPolicy, propName);
                                } else {
                                    entity2FlatMap(resultMap, propValue, ignoreNullProperty, null, keyNamingPolicy, parentPropName + WD.PERIOD + propName);
                                }
                            }
                        }

                        break;
                    }

                    case LOWER_CASE_WITH_UNDERSCORE: {
                        for (Map.Entry entry : getterMethodList.entrySet()) {
                            propName = entry.getKey();

                            if (hasIgnoredPropNames && ignoredPropNames.contains(propName)) {
                                continue;
                            }

                            propName = ClassUtil.toLowerCaseWithUnderscore(propName);
                            propValue = entry.getValue().invoke(entity);

                            if (ignoreNullProperty && (propValue == null)) {
                                continue;
                            }

                            if ((propValue == null) || !N.isEntity(propValue.getClass())) {
                                if (isNullParentPropName) {
                                    resultMap.put(propName, propValue);
                                } else {
                                    resultMap.put(parentPropName + WD.PERIOD + propName, propValue);
                                }
                            } else {
                                if (isNullParentPropName) {
                                    entity2FlatMap(resultMap, propValue, ignoreNullProperty, null, keyNamingPolicy, propName);
                                } else {
                                    entity2FlatMap(resultMap, propValue, ignoreNullProperty, null, keyNamingPolicy, parentPropName + WD.PERIOD + propName);
                                }
                            }
                        }

                        break;
                    }

                    case UPPER_CASE_WITH_UNDERSCORE: {
                        for (Map.Entry entry : getterMethodList.entrySet()) {
                            propName = entry.getKey();

                            if (hasIgnoredPropNames && ignoredPropNames.contains(propName)) {
                                continue;
                            }

                            propName = ClassUtil.toUpperCaseWithUnderscore(propName);
                            propValue = entry.getValue().invoke(entity);

                            if (ignoreNullProperty && (propValue == null)) {
                                continue;
                            }

                            if ((propValue == null) || !N.isEntity(propValue.getClass())) {
                                if (isNullParentPropName) {
                                    resultMap.put(propName, propValue);
                                } else {
                                    resultMap.put(parentPropName + WD.PERIOD + propName, propValue);
                                }
                            } else {
                                if (isNullParentPropName) {
                                    entity2FlatMap(resultMap, propValue, ignoreNullProperty, null, keyNamingPolicy, propName);
                                } else {
                                    entity2FlatMap(resultMap, propValue, ignoreNullProperty, null, keyNamingPolicy, parentPropName + WD.PERIOD + propName);
                                }
                            }
                        }

                        break;
                    }

                    default:
                        throw new IllegalArgumentException("Unsupported NamingPolicy: " + keyNamingPolicy);
                }
            } catch (Exception e) {
                throw N.toRuntimeException(e);
            }
        }

        return resultMap;
    }

    public static List> entity2FlatMap(final Collection entityList) {
        return entity2FlatMap(entityList, false);
    }

    public static List> entity2FlatMap(final Collection entityList, final boolean ignoreNullProperty) {
        return entity2FlatMap(entityList, ignoreNullProperty, null);
    }

    public static List> entity2FlatMap(final Collection entityList, final Collection ignoredPropNames) {
        return entity2FlatMap(entityList, false, ignoredPropNames);
    }

    public static List> entity2FlatMap(final Collection entityList, final boolean ignoreNullProperty,
            final Collection ignoredPropNames) {
        return entity2FlatMap(entityList, ignoreNullProperty, ignoredPropNames, NamingPolicy.LOWER_CAMEL_CASE);
    }

    public static List> entity2FlatMap(final Collection entityList, final boolean ignoreNullProperty,
            final Collection ignoredPropNames, final NamingPolicy keyNamingPolicy) {
        final List> resultList = new ArrayList<>(entityList.size());

        for (Object entity : entityList) {
            resultList.add(entity2FlatMap(entity, ignoreNullProperty, ignoredPropNames, keyNamingPolicy));
        }

        return resultList;
    }

    public static Map flatten(Map map) {
        return flatten(map, Suppliers. ofMap());
    }

    public static > M flatten(Map map, Supplier mapSupplier) {
        return flatten(map, ".", mapSupplier);
    }

    public static > M flatten(Map map, String delimiter, Supplier mapSupplier) {
        final M result = mapSupplier.get();

        flatten(map, null, delimiter, result);

        return result;
    }

    private static void flatten(Map map, String prefix, String delimiter, Map output) {
        if (N.isNullOrEmpty(map)) {
            return;
        }

        if (N.isNullOrEmpty(prefix)) {
            for (Map.Entry entry : map.entrySet()) {
                if (entry.getValue() instanceof Map) {
                    flatten((Map) entry.getValue(), entry.getKey(), delimiter, output);
                } else {
                    output.put(entry.getKey(), entry.getValue());
                }
            }
        } else {
            for (Map.Entry entry : map.entrySet()) {
                if (entry.getValue() instanceof Map) {
                    flatten((Map) entry.getValue(), prefix + delimiter + entry.getKey(), delimiter, output);
                } else {
                    output.put(prefix + delimiter + entry.getKey(), entry.getValue());
                }
            }
        }
    }

    public static Map unflatten(Map map) {
        return unflatten(map, Suppliers. ofMap());
    }

    public static > M unflatten(Map map, Supplier mapSupplier) {
        return unflatten(map, ".", mapSupplier);
    }

    public static > M unflatten(Map map, String delimiter, Supplier mapSupplier) {
        final M result = mapSupplier.get();
        final Splitter keySplitter = Splitter.with(delimiter);

        if (N.notNullOrEmpty(map)) {
            for (Map.Entry entry : map.entrySet()) {
                if (entry.getKey().indexOf(delimiter) >= 0) {
                    final String[] keys = keySplitter.splitToArray(entry.getKey());
                    Map lastMap = result;

                    for (int i = 0, to = keys.length - 1; i < to; i++) {
                        Map tmp = (Map) lastMap.get(keys[i]);

                        if (tmp == null) {
                            tmp = mapSupplier.get();
                            lastMap.put(keys[i], tmp);
                        }

                        lastMap = tmp;
                    }

                    lastMap.put(keys[keys.length - 1], entry.getValue());
                } else {
                    result.put(entry.getKey(), entry.getValue());
                }
            }
        }

        return result;
    }

    @SuppressWarnings("rawtypes")
    static Supplier mapType2Supplier(final Class mapType) {
        if (HashMap.class.equals(mapType)) {
            return Suppliers.ofMap();
        } else if (SortedMap.class.isAssignableFrom(mapType)) {
            return Suppliers.ofTreeMap();
        } else if (IdentityHashMap.class.isAssignableFrom(mapType)) {
            return Suppliers.ofIdentityHashMap();
        } else if (LinkedHashMap.class.isAssignableFrom(mapType)) {
            return Suppliers.ofLinkedHashMap();
        } else if (ImmutableMap.class.isAssignableFrom(mapType)) {
            return Suppliers.ofLinkedHashMap();
        } else {
            return new Supplier() {
                @Override
                public Map get() {
                    try {
                        return N.newInstance(mapType);
                    } catch (Exception e) {
                        return new LinkedHashMap<>();
                    }
                }
            };
        }
    }

    static  void replaceAll(Map map, BiFunction function) {
        N.checkArgNotNull(function);

        try {
            for (Map.Entry entry : map.entrySet()) {
                entry.setValue(function.apply(entry.getKey(), entry.getValue()));
            }
        } catch (IllegalStateException ise) {
            throw new ConcurrentModificationException(ise);
        }
    }

    static  void merge(Map map, K key, V value, Try.BiFunction remappingFunction)
            throws E {
        final V oldValue = map.get(key);

        if (oldValue == null && map.containsKey(key) == false) {
            map.put(key, value);
        } else {
            map.put(key, remappingFunction.apply(oldValue, value));
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy