com.github.dozermapper.core.util.MappingUtils.java Source code

Java tutorial

Introduction

Here is the source code for com.github.dozermapper.core.util.MappingUtils.java

Source

/*
 * Copyright 2005-2018 Dozer Project
 *
 * 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.github.dozermapper.core.util;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.github.dozermapper.core.MappingException;
import com.github.dozermapper.core.cache.Cache;
import com.github.dozermapper.core.classmap.ClassMap;
import com.github.dozermapper.core.classmap.Configuration;
import com.github.dozermapper.core.classmap.CopyByReferenceContainer;
import com.github.dozermapper.core.classmap.DozerClass;
import com.github.dozermapper.core.config.BeanContainer;
import com.github.dozermapper.core.converters.CustomConverterContainer;
import com.github.dozermapper.core.fieldmap.DozerField;
import com.github.dozermapper.core.fieldmap.FieldMap;

import org.apache.commons.lang3.StringUtils;

import static com.github.dozermapper.core.util.DozerConstants.BASE_CLASS;

/**
 * Internal class that provides various mapping utilities used throughout the code base. Only intended for internal use.
 */
public final class MappingUtils {

    private MappingUtils() {
    }

    public static String getClassNameWithoutPackage(Class<?> clazz) { // TODO Replace with Apache implementation
        Package pckage = clazz.getPackage();
        int pckageIndex = 0;
        if (pckage != null) {
            pckageIndex = pckage.getName().length() + 1;
        }
        return clazz.getName().substring(pckageIndex);
    }

    public static boolean isSupportedCollection(Class<?> aClass) {
        return CollectionUtils.isCollection(aClass) || CollectionUtils.isArray(aClass);
    }

    public static boolean isSupportedMap(Class<?> aClass) {
        return Map.class.isAssignableFrom(aClass);
    }

    public static void throwMappingException(Throwable e) throws MappingException {
        if (e instanceof MappingException) {
            // in this case we do not want to re-wrap an existing mapping exception
            throw (MappingException) e;
        } else if (e instanceof RuntimeException) {
            // feature request 1561837. Dont wrap any runtime exceptions in a MappingException
            throw (RuntimeException) e;
        } else {
            throw new MappingException(e);
        }
    }

    public static void throwMappingException(String msg) throws MappingException {
        throw new MappingException(msg);
    }

    public static void throwMappingException(String msg, Throwable cause) throws MappingException {
        throw new MappingException(msg, cause);
    }

    public static boolean isBlankOrNull(String value) {
        return value == null || value.trim().length() < 1;
    }

    public static Throwable getRootCause(Throwable ex) {
        Throwable rootCause = ex;
        while (rootCause.getCause() != null) {
            rootCause = rootCause.getCause();
        }
        return rootCause;
    }

    public static String getMappedParentFieldKey(Object destObj, FieldMap destFieldMap) {
        StringBuilder buf = new StringBuilder(100); // TODO Use IdentityHashMap instead of String concatenation
        buf.append(System.identityHashCode(destObj));
        buf.append(destFieldMap.getDestFieldName());
        if (destFieldMap.getDestFieldKey() != null) {
            buf.append("[").append(destFieldMap.getDestFieldKey()).append("]");
        }
        return buf.toString();
    }

    public static Class<?> findCustomConverter(Cache converterByDestTypeCache,
            CustomConverterContainer customConverterContainer, Class<?> srcClass, Class<?> destClass) {
        if (customConverterContainer == null) {
            return null;
        }

        return customConverterContainer.getCustomConverter(srcClass, destClass, converterByDestTypeCache);
    }

    public static Class<?> determineCustomConverter(FieldMap fieldMap, Cache converterByDestTypeCache,
            CustomConverterContainer customConverterContainer, Class<?> srcClass, Class<?> destClass) {
        if (customConverterContainer == null) {
            return null;
        }

        // This method is messy. Just trying to isolate the junk into this one method instead of spread across the mapping
        // processor until a better solution can be put into place
        // For indexed mapping, need to use the actual class at index to determine the custom converter.
        if (fieldMap != null && fieldMap.isDestFieldIndexed()) {
            if (destClass.isArray()) {
                destClass = destClass.getComponentType();
            } else if (destClass.isAssignableFrom(Collection.class) && fieldMap.getDestHintContainer() != null
                    && !fieldMap.getDestHintContainer().hasMoreThanOneHint()) {
                // use hint when trying to find a custom converter
                destClass = fieldMap.getDestHintContainer().getHint();
            }
        }

        return findCustomConverter(converterByDestTypeCache, customConverterContainer, srcClass, destClass);
    }

    public static void reverseFields(FieldMap source, FieldMap reversed) {
        DozerField destField = source.getSrcFieldCopy();
        DozerField sourceField = source.getDestFieldCopy();

        reversed.setDestField(destField);
        reversed.setSrcField(sourceField);

        reversed.setCustomConverter(source.getCustomConverter());
        reversed.setCustomConverterId(source.getCustomConverterId());
        reversed.setMapId(source.getMapId());
        reversed.setRelationshipType(source.getRelationshipType());
        reversed.setRemoveOrphans(source.isRemoveOrphans());
        reversed.setSrcHintContainer(source.getDestHintContainer());
        reversed.setDestHintContainer(source.getSrcHintContainer());
        reversed.setSrcDeepIndexHintContainer(source.getDestDeepIndexHintContainer());
        reversed.setDestDeepIndexHintContainer(source.getSrcDeepIndexHintContainer());
    }

    public static void reverseFields(ClassMap source, ClassMap destination, BeanContainer beanContainer) {
        // reverse the fields
        destination.setSrcClass(new DozerClass(source.getDestClassName(), source.getDestClassToMap(),
                source.getDestClassBeanFactory(), source.getDestClassBeanFactoryId(),
                source.getDestClassMapGetMethod(), source.getDestClassMapSetMethod(),
                source.getDestClass().getCreateMethod(), source.isDestMapNull(), source.isDestMapEmptyString(),
                source.getDestClass().isAccessible(), source.getDestClass().isSkipConstructor(), beanContainer));
        destination.setDestClass(new DozerClass(source.getSrcClassName(), source.getSrcClassToMap(),
                source.getSrcClassBeanFactory(), source.getSrcClassBeanFactoryId(),
                source.getSrcClassMapGetMethod(), source.getSrcClassMapSetMethod(),
                source.getSrcClass().getCreateMethod(), source.isSrcMapNull(), source.isSrcMapEmptyString(),
                source.getSrcClass().isAccessible(), source.getSrcClass().isSkipConstructor(), beanContainer));
        destination.setType(source.getType());
        destination.setWildcard(source.isWildcard());
        destination.setWildcardCaseInsensitive(source.isWildcardCaseInsensitive());
        destination.setTrimStrings(source.isTrimStrings());
        destination.setDateFormat(source.getDateFormat());
        destination.setRelationshipType(source.getRelationshipType());
        destination.setStopOnErrors(source.isStopOnErrors());
        destination.setAllowedExceptions(source.getAllowedExceptions());
        destination.setSrcClassCreateMethod(source.getDestClassCreateMethod());
        destination.setDestClassCreateMethod(source.getSrcClassCreateMethod());
        if (StringUtils.isNotEmpty(source.getMapId())) {
            destination.setMapId(source.getMapId());
        }
    }

    public static Object getIndexedValue(Object collection, int index) {
        Object result = null;
        if (collection instanceof Object[]) {
            Object[] x = (Object[]) collection;
            if (index < x.length) {
                return x[index];
            }
        } else if (collection instanceof Collection) {
            Collection<?> x = (Collection<?>) collection;
            if (index < x.size()) {
                Iterator<?> iter = x.iterator();
                for (int i = 0; i < index; i++) {
                    iter.next();
                }
                result = iter.next();
            }
        }
        return result;
    }

    public static void applyGlobalCopyByReference(Configuration globalConfig, FieldMap fieldMap,
            ClassMap classMap) {
        CopyByReferenceContainer copyByReferenceContainer = globalConfig.getCopyByReferences();
        String destFieldTypeName = null;
        Class<?> clazz = fieldMap.getDestFieldType(classMap.getDestClassToMap());
        if (clazz != null) {
            destFieldTypeName = clazz.getName();
        }
        if (copyByReferenceContainer.contains(destFieldTypeName) && !fieldMap.isCopyByReferenceOveridden()) {
            fieldMap.setCopyByReference(true);
        }
    }

    public static Class<?> loadClass(String name, BeanContainer beanContainer) {
        DozerClassLoader loader = beanContainer.getClassLoader();
        return loader.loadClass(name);
    }

    public static Class<?> getRealClass(Class<?> clazz, BeanContainer beanContainer) {
        DozerProxyResolver proxyResolver = beanContainer.getProxyResolver();
        return proxyResolver.getRealClass(clazz);
    }

    public static <T> T deProxy(T object, BeanContainer beanContainer) {
        DozerProxyResolver proxyResolver = beanContainer.getProxyResolver();
        return proxyResolver.unenhanceObject(object);
    }

    public static boolean isProxy(Class<?> clazz, BeanContainer beanContainer) {
        DozerProxyResolver proxyResolver = beanContainer.getProxyResolver();
        return proxyResolver.isProxy(clazz);
    }

    public static Object prepareIndexedCollection(Class<?> collectionType, Object existingCollection,
            Object collectionEntry, int index) {
        Object result = null;
        if (collectionType.isArray()) {
            result = prepareIndexedArray(collectionType, existingCollection, collectionEntry, index);
        } else if (Collection.class.isAssignableFrom(collectionType)) {
            result = prepareIndexedCollectionType(collectionType, existingCollection, collectionEntry, index);
        } else {
            throwMappingException(
                    "Only types java.lang.Object[] and java.util.Collection are supported for indexed properties.");
        }

        return result;
    }

    public static boolean isDeepMapping(String mapping) {
        return mapping != null && mapping.contains(DozerConstants.DEEP_FIELD_DELIMITER);
    }

    @SuppressWarnings("unchecked")
    private static <T> T[] prepareIndexedArray(Class<T> collectionType, Object existingCollection,
            Object collectionEntry, int index) {
        T[] result;

        if (existingCollection == null) {
            result = (T[]) Array.newInstance(collectionType.getComponentType(), index + 1);
        } else {
            int originalLenth = ((Object[]) existingCollection).length;
            result = (T[]) Array.newInstance(collectionType.getComponentType(), Math.max(index + 1, originalLenth));
            System.arraycopy(existingCollection, 0, result, 0, originalLenth);
        }
        result[index] = (T) collectionEntry;
        return result;
    }

    @SuppressWarnings("unchecked")
    private static Collection<?> prepareIndexedCollectionType(Class<?> collectionType, Object existingCollection,
            Object collectionEntry, int index) {
        Collection result = null;
        //Instantiation of the new Collection: can be interface or implementation class
        if (collectionType.isInterface()) {
            if (collectionType.equals(Set.class)) {
                result = new HashSet();
            } else if (collectionType.equals(List.class)) {
                result = new ArrayList();
            } else {
                throwMappingException(
                        "Only interface types java.util.Set and java.util.List are supported for java.util.Collection type indexed properties.");
            }
        } else {
            //It is an implementation class of Collection
            try {
                result = (Collection) collectionType.newInstance();
            } catch (InstantiationException e) {
                throw new RuntimeException(e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }

        //Fill in old values in new Collection
        if (existingCollection != null) {
            result.addAll((Collection) existingCollection);
        }

        //Add the new value:
        //For an ordered Collection
        if (result instanceof List) {
            while (result.size() < index + 1) {
                result.add(null);
            }
            ((List) result).set(index, collectionEntry);
        } else {
            //for an unordered Collection (index has no use here)
            result.add(collectionEntry);
        }
        return result;
    }

    /**
     * Used to test if {@code srcFieldClass} is enum.
     *
     * @param srcFieldClass the source field to be tested.
     * @return {@code true} if and only if current running JRE is 1.5 or above, and
     * {@code srcFieldClass} is enum; otherwise return {@code false}.
     */
    public static boolean isEnumType(Class<?> srcFieldClass) {
        if (srcFieldClass.isAnonymousClass()) {
            //If srcFieldClass is anonymous class, replace srcFieldClass with its enclosing class.
            //This is used to ensure Dozer can get correct Enum type.
            srcFieldClass = srcFieldClass.getEnclosingClass();
        }
        return srcFieldClass.isEnum();
    }

    /**
     * Used to test if both {@code srcFieldClass} and {@code destFieldType} are enum.
     *
     * @param srcFieldClass the source field to be tested.
     * @param destFieldType the destination field to be tested.
     * @return {@code true} if and only if current running JRE is 1.5 or above, and both
     * {@code srcFieldClass} and {@code destFieldType} are enum; otherwise return {@code false}.
     */
    public static boolean isEnumType(Class<?> srcFieldClass, Class<?> destFieldType) {
        return isEnumType(srcFieldClass) && isEnumType(destFieldType);
    }

    public static List<Class<?>> getSuperClassesAndInterfaces(Class<?> srcClass, BeanContainer beanContainer) {
        List<Class<?>> superClasses = new ArrayList<>();
        Class<?> realClass = getRealClass(srcClass, beanContainer);

        // Add all super classes first
        Class<?> superClass = getRealClass(realClass, beanContainer).getSuperclass();
        while (!isBaseClass(superClass)) {
            superClasses.add(superClass);
            superClass = superClass.getSuperclass();
        }

        // Now add all interfaces of the passed in class and all it's super classes

        // Linked hash set so duplicated are not added but insertion order is kept
        LinkedHashSet<Class<?>> interfaces = new LinkedHashSet<>();

        interfaces.addAll(getInterfaceHierarchy(realClass, beanContainer));
        for (Class<?> clazz : superClasses) {
            interfaces.addAll(getInterfaceHierarchy(clazz, beanContainer));
        }

        superClasses.addAll(interfaces);
        return superClasses;
    }

    @SuppressWarnings("unchecked")
    public static List<Class<?>> getInterfaceHierarchy(Class<?> srcClass, BeanContainer beanContainer) {
        final List<Class<?>> result = new LinkedList<>();
        Class<?> realClass = getRealClass(srcClass, beanContainer);

        final LinkedList<Class> interfacesToProcess = new LinkedList<>();

        Class[] interfaces = realClass.getInterfaces();

        interfacesToProcess.addAll(Arrays.asList(interfaces));

        while (!interfacesToProcess.isEmpty()) {
            Class<?> iface = interfacesToProcess.remove();
            if (!result.contains(iface)) {
                result.add(iface);
                for (Class subiface : iface.getInterfaces()) {
                    // if we haven't processed this interface yet then add it to be processed
                    if (!result.contains(subiface)) {
                        interfacesToProcess.add(subiface);
                    }
                }
            }
        }

        return result;

    }

    private static boolean isBaseClass(Class<?> clazz) {
        return clazz == null || BASE_CLASS.equals(clazz.getName());
    }

}