org.dozer.classmap.ClassMapBuilder.java Source code

Java tutorial

Introduction

Here is the source code for org.dozer.classmap.ClassMapBuilder.java

Source

/*
 * Copyright 2005-2010 the original author or authors.
 *
 * 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 org.dozer.classmap;

import org.apache.commons.lang3.StringUtils;
import org.dozer.Mapping;
import org.dozer.MappingException;
import org.dozer.classmap.generator.BeanMappingGenerator;
import org.dozer.classmap.generator.GeneratorUtils;
import org.dozer.fieldmap.DozerField;
import org.dozer.fieldmap.FieldMap;
import org.dozer.fieldmap.GenericFieldMap;
import org.dozer.fieldmap.MapFieldMap;
import org.dozer.util.DozerConstants;
import org.dozer.util.MappingUtils;
import org.dozer.util.ReflectionUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;

/**
 * Internal class for adding implicit field mappings to a ClassMap. Also, builds implicit ClassMap for class mappings
 * that don't have an explicit custom xml mapping. Only intended for internal use.
 *
 * @author tierney.matt
 * @author garsombke.franz
 */
public final class ClassMapBuilder {

    static final List<ClassMappingGenerator> buildTimeGenerators = new ArrayList<ClassMappingGenerator>();
    static final List<ClassMappingGenerator> runTimeGenerators = new ArrayList<ClassMappingGenerator>();

    static {
        buildTimeGenerators.add(new AnnotationPropertiesGenerator());
        buildTimeGenerators.add(new AnnotationFieldsGenerator());
        buildTimeGenerators.add(new MapMappingGenerator());
        buildTimeGenerators.add(new BeanMappingGenerator());
        buildTimeGenerators.add(new CollectionMappingGenerator());

        runTimeGenerators.add(new AnnotationPropertiesGenerator());
        runTimeGenerators.add(new AnnotationFieldsGenerator());
        runTimeGenerators.add(new MapMappingGenerator());
        runTimeGenerators.add(new BeanMappingGenerator());
    }

    private ClassMapBuilder() {
    }

    // TODO Cover with test cases
    // TODO Remove duplication
    // TODO Use Dozer Builder if possible ?
    // TODO Add Exclude Annotation process by separate generator
    // TODO Add Pluggable Buidlers
    // TODO Add field matcher based builder
    // TODO Add annotation based builder

    /**
     * Builds new default mapping on-the-fly for previously unknown mapped class pairs.
     *
     * @param globalConfiguration
     * @param srcClass
     * @param destClass
     * @return
     */
    public static ClassMap createDefaultClassMap(Configuration globalConfiguration, Class<?> srcClass,
            Class<?> destClass) {
        ClassMap classMap = new ClassMap(globalConfiguration);
        classMap.setSrcClass(
                new DozerClass(srcClass.getName(), srcClass, globalConfiguration.getBeanFactory(), null, null, null,
                        null, globalConfiguration.getMapNull(), globalConfiguration.getMapEmptyString(), false));
        classMap.setDestClass(new DozerClass(destClass.getName(), destClass, globalConfiguration.getBeanFactory(),
                null, null, null, null, globalConfiguration.getMapNull(), globalConfiguration.getMapEmptyString(),
                false));

        generateMapping(classMap, globalConfiguration, buildTimeGenerators);
        return classMap;
    }

    /**
     * Prepares default mappings based on provided mapping definition
     *
     * @param classMappings
     * @param globalConfiguration
     */
    public static void addDefaultFieldMappings(ClassMappings classMappings, Configuration globalConfiguration) {
        Set<Entry<String, ClassMap>> entries = classMappings.getAll().entrySet();
        for (Entry<String, ClassMap> entry : entries) {
            ClassMap classMap = entry.getValue();
            generateMapping(classMap, globalConfiguration, runTimeGenerators);
        }
    }

    private static void generateMapping(ClassMap classMap, Configuration configuration,
            List<ClassMappingGenerator> mappingGenerators) {
        if (!classMap.isWildcard()) {
            return;
        }

        for (ClassMappingGenerator generator : mappingGenerators) {
            if (generator.accepts(classMap)) {
                if (generator.apply(classMap, configuration)) {
                    return;
                }
            }
        }
    }

    public static interface ClassMappingGenerator {

        boolean accepts(ClassMap classMap);

        /**
         *
         * @return true if we should stop after applied
         */
        boolean apply(ClassMap classMap, Configuration configuration);

    }

    public static class MapMappingGenerator implements ClassMappingGenerator {

        public boolean accepts(ClassMap classMap) {
            Class<?> srcClass = classMap.getSrcClassToMap();
            Class<?> destClass = classMap.getDestClassToMap();

            return MappingUtils.isSupportedMap(srcClass) || classMap.getSrcClassMapGetMethod() != null
                    || MappingUtils.isSupportedMap(destClass) || classMap.getDestClassMapGetMethod() != null;
        }

        public boolean apply(ClassMap classMap, Configuration configuration) {
            Class<?> srcClass = classMap.getSrcClassToMap();
            Class<?> destClass = classMap.getDestClassToMap();
            PropertyDescriptor[] properties;
            boolean destinationIsMap = false;

            if (MappingUtils.isSupportedMap(srcClass) || classMap.getSrcClassMapGetMethod() != null) {
                properties = ReflectionUtils.getPropertyDescriptors(destClass);
            } else {
                properties = ReflectionUtils.getPropertyDescriptors(srcClass);
                destinationIsMap = true;
            }

            for (PropertyDescriptor property : properties) {
                String fieldName = property.getName();

                if (GeneratorUtils.shouldIgnoreField(fieldName, srcClass, destClass)) {
                    continue;
                }

                // already mapped
                if (destinationIsMap && classMap.getFieldMapUsingSrc(fieldName) != null) {
                    continue;
                }

                // already mapped
                if (!destinationIsMap && classMap.getFieldMapUsingDest(fieldName, true) != null) {
                    continue;
                }

                FieldMap fieldMap = new MapFieldMap(classMap);
                DozerField srcField = new DozerField(
                        MappingUtils.isSupportedMap(srcClass) ? DozerConstants.SELF_KEYWORD : fieldName, null);
                srcField.setKey(fieldName);

                if (StringUtils.isNotEmpty(classMap.getSrcClassMapGetMethod())
                        || StringUtils.isNotEmpty(classMap.getSrcClassMapSetMethod())) {
                    srcField.setMapGetMethod(classMap.getSrcClassMapGetMethod());
                    srcField.setMapSetMethod(classMap.getSrcClassMapSetMethod());
                    srcField.setName(DozerConstants.SELF_KEYWORD);
                }

                DozerField destField = new DozerField(
                        MappingUtils.isSupportedMap(destClass) ? DozerConstants.SELF_KEYWORD : fieldName, null);
                srcField.setKey(fieldName);

                if (StringUtils.isNotEmpty(classMap.getDestClassMapGetMethod())
                        || StringUtils.isNotEmpty(classMap.getDestClassMapSetMethod())) {
                    destField.setMapGetMethod(classMap.getDestClassMapGetMethod());
                    destField.setMapSetMethod(classMap.getDestClassMapSetMethod());
                    destField.setName(DozerConstants.SELF_KEYWORD);
                }

                fieldMap.setSrcField(srcField);
                fieldMap.setDestField(destField);

                classMap.addFieldMapping(fieldMap);
            }
            return true;
        }
    }

    public static class CollectionMappingGenerator implements ClassMappingGenerator {

        public boolean accepts(ClassMap classMap) {
            Class<?> srcClass = classMap.getSrcClassToMap();
            Class<?> destClass = classMap.getDestClassToMap();
            return MappingUtils.isSupportedCollection(srcClass) && MappingUtils.isSupportedCollection(destClass);
        }

        public boolean apply(ClassMap classMap, Configuration configuration) {
            FieldMap fieldMap = new GenericFieldMap(classMap);
            DozerField selfReference = new DozerField(DozerConstants.SELF_KEYWORD, null);
            fieldMap.setSrcField(selfReference);
            fieldMap.setDestField(selfReference);
            classMap.addFieldMapping(fieldMap);
            return true;
        }
    }

    public static class AnnotationPropertiesGenerator implements ClassMappingGenerator {

        public boolean accepts(ClassMap classMap) {
            return true;
        }

        public boolean apply(ClassMap classMap, Configuration configuration) {
            Class<?> srcType = classMap.getSrcClassToMap();
            PropertyDescriptor[] srcProperties = ReflectionUtils.getPropertyDescriptors(srcType);
            for (PropertyDescriptor property : srcProperties) {
                Method readMethod = property.getReadMethod();
                if (readMethod != null) {
                    Mapping mapping = readMethod.getAnnotation(Mapping.class);
                    String propertyName = property.getName();
                    if (mapping != null) {
                        validate(mapping, readMethod);
                        String pairName = mapping.value();
                        GeneratorUtils.addGenericMapping(classMap, configuration, propertyName, pairName);
                    }
                }
            }

            Class<?> destType = classMap.getDestClassToMap();
            PropertyDescriptor[] destProperties = ReflectionUtils.getPropertyDescriptors(destType);
            for (PropertyDescriptor property : destProperties) {
                Method readMethod = property.getReadMethod();
                if (readMethod != null) {
                    Mapping mapping = readMethod.getAnnotation(Mapping.class);
                    String propertyName = property.getName();
                    if (mapping != null) {
                        validate(mapping, readMethod);
                        String pairName = mapping.value();
                        GeneratorUtils.addGenericMapping(classMap, configuration, pairName, propertyName);
                    }
                }
            }

            return false;
        }
    }

    public static class AnnotationFieldsGenerator implements ClassMappingGenerator {

        public boolean accepts(ClassMap classMap) {
            return true;
        }

        public boolean apply(ClassMap classMap, Configuration configuration) {
            Class<?> srcType = classMap.getSrcClassToMap();
            Field[] srcFields = srcType.getDeclaredFields();
            for (Field field : srcFields) {
                Mapping mapping = field.getAnnotation(Mapping.class);
                String fieldName = field.getName();
                if (mapping != null) {
                    validate(mapping, field);
                    String pairName = mapping.value();
                    addFieldMapping(classMap, configuration, fieldName, pairName);
                }
            }

            Class<?> destType = classMap.getDestClassToMap();
            Field[] destFields = destType.getDeclaredFields();
            for (Field field : destFields) {
                Mapping mapping = field.getAnnotation(Mapping.class);
                String fieldName = field.getName();
                if (mapping != null) {
                    validate(mapping, field);
                    String pairName = mapping.value();
                    addFieldMapping(classMap, configuration, pairName, fieldName);
                }
            }

            return false;
        }
    }

    private static void addFieldMapping(ClassMap classMap, Configuration configuration, String srcName,
            String destName) {
        FieldMap fieldMap = new GenericFieldMap(classMap);

        DozerField sourceField = new DozerField(srcName, null);
        DozerField destField = new DozerField(destName, null);

        sourceField.setAccessible(true);
        destField.setAccessible(true);

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

        // add CopyByReferences per defect #1728159
        MappingUtils.applyGlobalCopyByReference(configuration, fieldMap, classMap);
        classMap.addFieldMapping(fieldMap);
    }

    private static void validate(Mapping annotation, Member member) {
        if (annotation.value().trim().equals("")) {
            throw new MappingException("Mapping annotation value missing at " + member.getDeclaringClass().getName()
                    + "." + member.getName());
        }
    }

}