Package org.dozer.classmap

Source Code of org.dozer.classmap.ClassMapBuilder$AnnotationPropertiesGenerator

/*
* 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.lang.StringUtils;
import org.dozer.Mapping;
import org.dozer.MappingException;
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 {

  private static final String CLASS = "class";
  private static final String CALLBACK = "callback";
  private static final String CALLBACKS = "callbacks";

  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, DozerConstants.DEFAULT_MAP_NULL_POLICY, DozerConstants.DEFAULT_MAP_EMPTY_STRING_POLICY, false));
    classMap.setDestClass(new DozerClass(destClass.getName(), destClass, globalConfiguration.getBeanFactory(), null, null, null,
            null, DozerConstants.DEFAULT_MAP_NULL_POLICY, DozerConstants.DEFAULT_MAP_EMPTY_STRING_POLICY, 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;
        }
      }
    }
  }

  static boolean shouldIgnoreField(String fieldName, Class<?> srcType, Class<?> destType) {
    if (CLASS.equals(fieldName)) {
      return true;
    }
    if ((CALLBACK.equals(fieldName) || CALLBACKS.equals(fieldName))
            && (MappingUtils.isProxy(srcType) || MappingUtils.isProxy(destType))) {
      return true;
    }
    return false;
  }

  public static interface ClassMappingGenerator {

    boolean accepts(ClassMap classMap);

    // returns true if last in the chain

    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 (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 BeanMappingGenerator implements ClassMappingGenerator {

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

    public boolean apply(ClassMap classMap, Configuration configuration) {
      Class<?> srcClass = classMap.getSrcClassToMap();
      Class<?> destClass = classMap.getDestClassToMap();

      PropertyDescriptor[] destProperties = ReflectionUtils.getPropertyDescriptors(destClass);
      for (PropertyDescriptor destPropertyDescriptor : destProperties) {
        String fieldName = destPropertyDescriptor.getName();

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

        // If field has already been accounted for, then skip
        if (classMap.getFieldMapUsingDest(fieldName) != null
                || classMap.getFieldMapUsingSrc(fieldName) != null) {
          continue;
        }

        // If destination field does not have a write method, then skip
        if (destPropertyDescriptor.getWriteMethod() == null) {
          continue;
        }

        PropertyDescriptor srcProperty = ReflectionUtils.findPropertyDescriptor(srcClass, fieldName, null);

        // If the sourceProperty is null we know that there is not a corresponding property to map to.
        // If source property does not have a read method, then skip
        if (srcProperty == null || srcProperty.getReadMethod() == null) {
          continue;
        }

        addGenericMapping(classMap, configuration, fieldName, fieldName);
      }
      return false;
    }

  }

  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();
            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();
            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 addGenericMapping(ClassMap classMap, Configuration configuration,
                                        String srcName, String destName) {
    FieldMap fieldMap = new GenericFieldMap(classMap);

    fieldMap.setSrcField(new DozerField(srcName, null));
    fieldMap.setDestField(new DozerField(destName, null));

    // 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());
    }
  }

}
TOP

Related Classes of org.dozer.classmap.ClassMapBuilder$AnnotationPropertiesGenerator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.