Coverage Report - org.dozer.classmap.ClassMapBuilder
 
Classes in this File Line Coverage Branch Coverage Complexity
ClassMapBuilder
95%
42/44
100%
10/10
3.667
ClassMapBuilder$AnnotationFieldsGenerator
100%
21/21
100%
16/16
3.667
ClassMapBuilder$AnnotationPropertiesGenerator
100%
23/23
87%
14/16
3.667
ClassMapBuilder$ClassMappingGenerator
N/A
N/A
3.667
ClassMapBuilder$CollectionMappingGenerator
100%
10/10
75%
3/4
3.667
ClassMapBuilder$MapMappingGenerator
100%
36/36
94%
34/36
3.667
 
 1  
 /**
 2  
  * Copyright 2005-2013 Dozer Project
 3  
  *
 4  
  * Licensed under the Apache License, Version 2.0 (the "License");
 5  
  * you may not use this file except in compliance with the License.
 6  
  * You may obtain a copy of the License at
 7  
  *
 8  
  *      http://www.apache.org/licenses/LICENSE-2.0
 9  
  *
 10  
  * Unless required by applicable law or agreed to in writing, software
 11  
  * distributed under the License is distributed on an "AS IS" BASIS,
 12  
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13  
  * See the License for the specific language governing permissions and
 14  
  * limitations under the License.
 15  
  */
 16  
 package org.dozer.classmap;
 17  
 
 18  
 import org.apache.commons.lang3.StringUtils;
 19  
 import org.dozer.Mapping;
 20  
 import org.dozer.MappingException;
 21  
 import org.dozer.classmap.generator.BeanMappingGenerator;
 22  
 import org.dozer.classmap.generator.GeneratorUtils;
 23  
 import org.dozer.fieldmap.DozerField;
 24  
 import org.dozer.fieldmap.FieldMap;
 25  
 import org.dozer.fieldmap.GenericFieldMap;
 26  
 import org.dozer.fieldmap.MapFieldMap;
 27  
 import org.dozer.util.DozerConstants;
 28  
 import org.dozer.util.MappingUtils;
 29  
 import org.dozer.util.ReflectionUtils;
 30  
 
 31  
 import java.beans.PropertyDescriptor;
 32  
 import java.lang.reflect.Field;
 33  
 import java.lang.reflect.Member;
 34  
 import java.lang.reflect.Method;
 35  
 import java.util.ArrayList;
 36  
 import java.util.List;
 37  
 import java.util.Map.Entry;
 38  
 import java.util.Set;
 39  
 
 40  
 /**
 41  
  * Internal class for adding implicit field mappings to a ClassMap. Also, builds implicit ClassMap for class mappings
 42  
  * that don't have an explicit custom xml mapping. Only intended for internal use.
 43  
  *
 44  
  * @author tierney.matt
 45  
  * @author garsombke.franz
 46  
  */
 47  64
 public final class ClassMapBuilder {
 48  
 
 49  1
   static final List<ClassMappingGenerator> buildTimeGenerators = new ArrayList<ClassMappingGenerator>();
 50  1
   static final List<ClassMappingGenerator> runTimeGenerators = new ArrayList<ClassMappingGenerator>();
 51  
 
 52  
   static {
 53  1
     buildTimeGenerators.add(new AnnotationPropertiesGenerator());
 54  1
     buildTimeGenerators.add(new AnnotationFieldsGenerator());
 55  1
     buildTimeGenerators.add(new MapMappingGenerator());
 56  1
     buildTimeGenerators.add(new BeanMappingGenerator());
 57  1
     buildTimeGenerators.add(new CollectionMappingGenerator());
 58  
 
 59  1
     runTimeGenerators.add(new AnnotationPropertiesGenerator());
 60  1
     runTimeGenerators.add(new AnnotationFieldsGenerator());
 61  1
     runTimeGenerators.add(new MapMappingGenerator());
 62  1
     runTimeGenerators.add(new BeanMappingGenerator());
 63  1
   }
 64  
 
 65  0
   private ClassMapBuilder() {
 66  0
   }
 67  
 
 68  
   // TODO Cover with test cases
 69  
   // TODO Remove duplication
 70  
   // TODO Use Dozer Builder if possible ?
 71  
   // TODO Add Exclude Annotation process by separate generator
 72  
   // TODO Add Pluggable Buidlers
 73  
   // TODO Add field matcher based builder
 74  
   // TODO Add annotation based builder
 75  
 
 76  
   /**
 77  
    * Builds new default mapping on-the-fly for previously unknown mapped class pairs.
 78  
    *
 79  
    * @param globalConfiguration
 80  
    * @param srcClass
 81  
    * @param destClass
 82  
    * @return
 83  
    */
 84  
   public static ClassMap createDefaultClassMap(Configuration globalConfiguration, Class<?> srcClass, Class<?> destClass) {
 85  460
     ClassMap classMap = new ClassMap(globalConfiguration);
 86  460
     classMap.setSrcClass(new DozerClass(srcClass.getName(), srcClass, globalConfiguration.getBeanFactory(), null, null, null, null,
 87  
         globalConfiguration.getMapNull(), globalConfiguration.getMapEmptyString(), false));
 88  460
     classMap.setDestClass(new DozerClass(destClass.getName(), destClass, globalConfiguration.getBeanFactory(), null, null, null,
 89  
         null, globalConfiguration.getMapNull(), globalConfiguration.getMapEmptyString(), false));
 90  
 
 91  460
     generateMapping(classMap, globalConfiguration, buildTimeGenerators);
 92  460
     return classMap;
 93  
   }
 94  
 
 95  
   /**
 96  
    * Prepares default mappings based on provided mapping definition
 97  
    *
 98  
    * @param classMappings
 99  
    * @param globalConfiguration
 100  
    */
 101  
   public static void addDefaultFieldMappings(ClassMappings classMappings, Configuration globalConfiguration) {
 102  683
     Set<Entry<String, ClassMap>> entries = classMappings.getAll().entrySet();
 103  683
     for (Entry<String, ClassMap> entry : entries) {
 104  10573
       ClassMap classMap = entry.getValue();
 105  10573
       generateMapping(classMap, globalConfiguration, runTimeGenerators);
 106  10573
     }
 107  683
   }
 108  
 
 109  
   private static void generateMapping(ClassMap classMap, Configuration configuration, List<ClassMappingGenerator> mappingGenerators) {
 110  11033
     if (!classMap.isWildcard()) {
 111  1363
       return;
 112  
     }
 113  
 
 114  9670
     for (ClassMappingGenerator generator : mappingGenerators) {
 115  38094
       if (generator.accepts(classMap)) {
 116  29014
         if (generator.apply(classMap, configuration)) {
 117  1027
           return;
 118  
         }
 119  
       }
 120  37067
     }
 121  8643
   }
 122  
 
 123  
   public static interface ClassMappingGenerator {
 124  
 
 125  
     boolean accepts(ClassMap classMap);
 126  
 
 127  
     /**
 128  
      *
 129  
      * @return true if we should stop after applied
 130  
      */
 131  
     boolean apply(ClassMap classMap, Configuration configuration);
 132  
 
 133  
   }
 134  
 
 135  3
   public static class MapMappingGenerator implements ClassMappingGenerator {
 136  
 
 137  
     public boolean accepts(ClassMap classMap) {
 138  9670
       Class<?> srcClass = classMap.getSrcClassToMap();
 139  9670
       Class<?> destClass = classMap.getDestClassToMap();
 140  
 
 141  9670
       return MappingUtils.isSupportedMap(srcClass) || classMap.getSrcClassMapGetMethod() != null
 142  
           || MappingUtils.isSupportedMap(destClass) || classMap.getDestClassMapGetMethod() != null;
 143  
     }
 144  
 
 145  
     public boolean apply(ClassMap classMap, Configuration configuration) {
 146  1023
       Class<?> srcClass = classMap.getSrcClassToMap();
 147  1023
       Class<?> destClass = classMap.getDestClassToMap();
 148  
       PropertyDescriptor[] properties;
 149  1023
       boolean destinationIsMap = false;
 150  
 
 151  1023
       if (MappingUtils.isSupportedMap(srcClass) || classMap.getSrcClassMapGetMethod() != null) {
 152  523
         properties = ReflectionUtils.getPropertyDescriptors(destClass);
 153  
       } else {
 154  500
         properties = ReflectionUtils.getPropertyDescriptors(srcClass);
 155  500
         destinationIsMap = true;
 156  
       }
 157  
 
 158  13040
       for (PropertyDescriptor property : properties) {
 159  12017
         String fieldName = property.getName();
 160  
 
 161  12017
         if (GeneratorUtils.shouldIgnoreField(fieldName, srcClass, destClass)) {
 162  1027
           continue;
 163  
         }
 164  
 
 165  
         // already mapped
 166  10990
         if (destinationIsMap && classMap.getFieldMapUsingSrc(fieldName) != null) {
 167  926
           continue;
 168  
         }
 169  
 
 170  
         // already mapped
 171  10064
         if (!destinationIsMap && classMap.getFieldMapUsingDest(fieldName, true) != null) {
 172  944
           continue;
 173  
         }
 174  
 
 175  9120
         FieldMap fieldMap = new MapFieldMap(classMap);
 176  9120
         DozerField srcField = new DozerField(MappingUtils.isSupportedMap(srcClass) ? DozerConstants.SELF_KEYWORD : fieldName, null);
 177  9120
         srcField.setKey(fieldName);
 178  
 
 179  9120
         if (StringUtils.isNotEmpty(classMap.getSrcClassMapGetMethod())
 180  
             || StringUtils.isNotEmpty(classMap.getSrcClassMapSetMethod())) {
 181  2172
           srcField.setMapGetMethod(classMap.getSrcClassMapGetMethod());
 182  2172
           srcField.setMapSetMethod(classMap.getSrcClassMapSetMethod());
 183  2172
           srcField.setName(DozerConstants.SELF_KEYWORD);
 184  
         }
 185  
 
 186  9120
         DozerField destField = new DozerField(MappingUtils.isSupportedMap(destClass) ? DozerConstants.SELF_KEYWORD : fieldName,
 187  
             null);
 188  9120
         srcField.setKey(fieldName);
 189  
 
 190  9120
         if (StringUtils.isNotEmpty(classMap.getDestClassMapGetMethod())
 191  
             || StringUtils.isNotEmpty(classMap.getDestClassMapSetMethod())) {
 192  2172
           destField.setMapGetMethod(classMap.getDestClassMapGetMethod());
 193  2172
           destField.setMapSetMethod(classMap.getDestClassMapSetMethod());
 194  2172
           destField.setName(DozerConstants.SELF_KEYWORD);
 195  
         }
 196  
 
 197  9120
         fieldMap.setSrcField(srcField);
 198  9120
         fieldMap.setDestField(destField);
 199  
 
 200  9120
         classMap.addFieldMapping(fieldMap);
 201  
       }
 202  1023
       return true;
 203  
     }
 204  
   }
 205  
 
 206  2
   public static class CollectionMappingGenerator implements ClassMappingGenerator {
 207  
 
 208  
     public boolean accepts(ClassMap classMap) {
 209  437
       Class<?> srcClass = classMap.getSrcClassToMap();
 210  437
       Class<?> destClass = classMap.getDestClassToMap();
 211  437
       return MappingUtils.isSupportedCollection(srcClass) && MappingUtils.isSupportedCollection(destClass);
 212  
     }
 213  
 
 214  
     public boolean apply(ClassMap classMap, Configuration configuration) {
 215  5
       FieldMap fieldMap = new GenericFieldMap(classMap);
 216  5
       DozerField selfReference = new DozerField(DozerConstants.SELF_KEYWORD, null);
 217  5
       fieldMap.setSrcField(selfReference);
 218  5
       fieldMap.setDestField(selfReference);
 219  5
       classMap.addFieldMapping(fieldMap);
 220  5
       return true;
 221  
     }
 222  
   }
 223  
 
 224  2
   public static class AnnotationPropertiesGenerator implements ClassMappingGenerator {
 225  
 
 226  
     public boolean accepts(ClassMap classMap) {
 227  9670
       return true;
 228  
     }
 229  
 
 230  
     public boolean apply(ClassMap classMap, Configuration configuration) {
 231  9670
       Class<?> srcType = classMap.getSrcClassToMap();
 232  9670
       PropertyDescriptor[] srcProperties = ReflectionUtils.getPropertyDescriptors(srcType);
 233  69074
       for (PropertyDescriptor property : srcProperties) {
 234  59404
         Method readMethod = property.getReadMethod();
 235  59404
         if (readMethod != null) {
 236  59062
           Mapping mapping = readMethod.getAnnotation(Mapping.class);
 237  59062
           String propertyName = property.getName();
 238  59062
           if (mapping != null) {
 239  16
             String pairName = mapping.value().trim();
 240  16
             GeneratorUtils.addGenericMapping(classMap, configuration, propertyName, pairName.isEmpty() ? propertyName : pairName);
 241  
           }
 242  
         }
 243  
       }
 244  
 
 245  9670
       Class<?> destType = classMap.getDestClassToMap();
 246  9670
       PropertyDescriptor[] destProperties = ReflectionUtils.getPropertyDescriptors(destType);
 247  68274
       for (PropertyDescriptor property : destProperties) {
 248  58604
         Method readMethod = property.getReadMethod();
 249  58604
         if (readMethod != null) {
 250  58294
           Mapping mapping = readMethod.getAnnotation(Mapping.class);
 251  58294
           String propertyName = property.getName();
 252  58294
           if (mapping != null) {
 253  16
             String pairName = mapping.value().trim();
 254  16
             GeneratorUtils.addGenericMapping(classMap, configuration, pairName.isEmpty() ? propertyName : pairName, propertyName);
 255  
           }
 256  
         }
 257  
       }
 258  
 
 259  9670
       return false;
 260  
     }
 261  
   }
 262  
 
 263  2
   public static class AnnotationFieldsGenerator implements ClassMappingGenerator {
 264  
 
 265  
     public boolean accepts(ClassMap classMap) {
 266  9670
       return true;
 267  
     }
 268  
 
 269  
     public boolean apply(ClassMap classMap, Configuration configuration) {
 270  9670
       Class<?> srcType = classMap.getSrcClassToMap();
 271  
       do {
 272  86624
         for (Field field : srcType.getDeclaredFields()) {
 273  56379
           Mapping mapping = field.getAnnotation(Mapping.class);
 274  56379
           String fieldName = field.getName();
 275  56379
           if (mapping != null) {
 276  32
             String pairName = mapping.value().trim();
 277  32
             addFieldMapping(classMap, configuration, fieldName, pairName.isEmpty() ? fieldName : pairName);
 278  
           }
 279  
         }
 280  30245
         srcType = srcType.getSuperclass();
 281  30245
       } while (srcType != null);
 282  
       
 283  9670
       Class<?> destType = classMap.getDestClassToMap();
 284  
       do {
 285  83520
         for (Field field : destType.getDeclaredFields()) {
 286  53369
           Mapping mapping = field.getAnnotation(Mapping.class);
 287  53369
           String fieldName = field.getName();
 288  53369
           if (mapping != null) {
 289  32
             String pairName = mapping.value().trim();
 290  32
             addFieldMapping(classMap, configuration, pairName.isEmpty() ? fieldName : pairName, fieldName);
 291  
           }
 292  
         }
 293  30151
         destType = destType.getSuperclass();
 294  30151
       } while (destType != null);
 295  
       
 296  9670
       return false;
 297  
     }
 298  
   }
 299  
 
 300  
   private static void addFieldMapping(ClassMap classMap, Configuration configuration, String srcName, String destName) {
 301  64
     FieldMap fieldMap = new GenericFieldMap(classMap);
 302  
 
 303  64
     DozerField sourceField = new DozerField(srcName, null);
 304  64
     DozerField destField = new DozerField(destName, null);
 305  
 
 306  64
     sourceField.setAccessible(true);
 307  64
     destField.setAccessible(true);
 308  
 
 309  64
     fieldMap.setSrcField(sourceField);
 310  64
     fieldMap.setDestField(destField);
 311  
 
 312  
     // add CopyByReferences per defect #1728159
 313  64
     MappingUtils.applyGlobalCopyByReference(configuration, fieldMap, classMap);
 314  64
     classMap.addFieldMapping(fieldMap);
 315  64
   }
 316  
 }