Coverage Report - org.dozer.classmap.ClassMappings
 
Classes in this File Line Coverage Branch Coverage Complexity
ClassMappings
94%
55/58
96%
50/52
3.214
 
 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.util.MappingUtils;
 20  
 
 21  
 import java.lang.reflect.Modifier;
 22  
 import java.util.HashMap;
 23  
 import java.util.Map;
 24  
 import java.util.Map.Entry;
 25  
 import java.util.concurrent.ConcurrentHashMap;
 26  
 
 27  
 /**
 28  
  * Internal class that determines the appropriate class mapping to be used for
 29  
  * the source and destination object being mapped. Only intended for internal
 30  
  * use.
 31  
  *
 32  
  * @author tierney.matt
 33  
  * @author garsombke.franz
 34  
  */
 35  
 public class ClassMappings {
 36  
 
 37  
   // Cache key --> Mapping Structure
 38  1320
   private Map<String, ClassMap> classMappings = new ConcurrentHashMap<String, ClassMap>();
 39  
   private ClassMapKeyFactory keyFactory;
 40  
 
 41  1320
   public ClassMappings() {
 42  1320
     keyFactory = new ClassMapKeyFactory();
 43  1320
   }
 44  
 
 45  
   // Default mappings. May be ovewritten due to multiple threads generating same mapping
 46  
   public void addDefault(Class<?> srcClass, Class<?> destClass, ClassMap classMap) {
 47  460
     classMappings.put(keyFactory.createKey(srcClass, destClass), classMap);
 48  460
   }
 49  
 
 50  
   public void add(Class<?> srcClass, Class<?> destClass, ClassMap classMap) {
 51  7
     ClassMap result = classMappings.put(keyFactory.createKey(srcClass, destClass), classMap);
 52  7
     failOnDuplicate(result, classMap);
 53  6
   }
 54  
 
 55  
   public void add(Class<?> srcClass, Class<?> destClass, String mapId, ClassMap classMap) {
 56  10626
     ClassMap result = classMappings.put(keyFactory.createKey(srcClass, destClass, mapId), classMap);
 57  10626
     failOnDuplicate(result, classMap);
 58  10624
   }
 59  
 
 60  
   public void addAll(ClassMappings additionalClassMappings) {
 61  614
     Map<String, ClassMap> newMappings = additionalClassMappings.getAll();
 62  614
     for (Entry<String, ClassMap> entry : newMappings.entrySet()) {
 63  10579
       ClassMap result = classMappings.put(entry.getKey(), entry.getValue());
 64  10579
       failOnDuplicate(result, entry.getValue());
 65  10577
     }
 66  612
   }
 67  
 
 68  
   public void failOnDuplicate(Object result, ClassMap classMap) {
 69  21212
     if (result != null && !classMap.getSrcClassName().equals(classMap.getDestClassName())) {
 70  5
       throw new IllegalArgumentException("Duplicate Class Mapping Found. Source: " + classMap.getSrcClassName()
 71  
               + " Destination: " + classMap.getDestClassName() + " map-id: " + classMap.getMapId());
 72  
     }
 73  21207
   }
 74  
 
 75  
   public Map<String, ClassMap> getAll() {
 76  2013
     return new HashMap<String, ClassMap>(classMappings);
 77  
   }
 78  
 
 79  
   public long size() {
 80  1
     return classMappings.size();
 81  
   }
 82  
 
 83  
   public ClassMap find(Class<?> srcClass, Class<?> destClass) {
 84  11277
     return classMappings.get(keyFactory.createKey(srcClass, destClass));
 85  
   }
 86  
 
 87  
   public boolean contains(Class<?> srcClass, Class<?> destClass, String mapId) {
 88  0
     String key = keyFactory.createKey(srcClass, destClass, mapId);
 89  0
     return classMappings.containsKey(key);
 90  
   }
 91  
 
 92  
   public ClassMap find(Class<?> srcClass, Class<?> destClass, String mapId) {
 93  172967
     final String key = keyFactory.createKey(srcClass, destClass, mapId);
 94  172967
     ClassMap mapping = classMappings.get(key);
 95  
 
 96  172967
     if (mapping == null) {
 97  131612
       mapping = findInterfaceMapping(destClass, srcClass, mapId);
 98  
     }
 99  
 
 100  
     // one more try...
 101  
     // if the mapId is not null looking up a map is easy
 102  172967
     if (!MappingUtils.isBlankOrNull(mapId) && mapping == null) {
 103  
       // probably a more efficient way to do this...
 104  11
       for (Entry<String, ClassMap> entry : classMappings.entrySet()) {
 105  107
         ClassMap classMap = entry.getValue();
 106  107
         if (StringUtils.equals(classMap.getMapId(), mapId)
 107  
                 && classMap.getSrcClassToMap().isAssignableFrom(srcClass)
 108  
                 && classMap.getDestClassToMap().isAssignableFrom(destClass)) {
 109  9
           return classMap;
 110  98
         } else if (StringUtils.equals(classMap.getMapId(), mapId) && srcClass.equals(destClass)) {
 111  0
           return classMap;
 112  
         }
 113  98
       }
 114  
 
 115  
       // If map-id was specified and mapping was not found, then fail
 116  2
       MappingUtils.throwMappingException("Class mapping not found by map-id: " + key);
 117  
     }
 118  
 
 119  172956
     return mapping;
 120  
   }
 121  
 
 122  
   // Look for an interface mapping
 123  
   private ClassMap findInterfaceMapping(Class<?> destClass, Class<?> srcClass, String mapId) {
 124  
     // Use object array for keys to avoid any rare thread synchronization issues
 125  
     // while iterating over the custom mappings.
 126  
     // See bug #1550275.
 127  131612
     Object[] keys = classMappings.keySet().toArray();
 128  278151
     for (Object key : keys) {
 129  277680
       ClassMap map = classMappings.get(key);
 130  277680
       Class<?> mappingDestClass = map.getDestClassToMap();
 131  277680
       Class<?> mappingSrcClass = map.getSrcClassToMap();
 132  
 
 133  277680
       if ((mapId == null && map.getMapId() != null) || (mapId != null && !mapId.equals(map.getMapId()))) {
 134  438
         continue;
 135  
       }
 136  
 
 137  275601
       if (isInterfaceImplementation(srcClass, mappingSrcClass)) {
 138  131125
         if (isInterfaceImplementation(destClass, mappingDestClass)) {
 139  6
           return map;
 140  131119
         } else if (destClass.equals(mappingDestClass)) {
 141  131112
           return map;
 142  
         }
 143  
       }
 144  
 
 145  
       // Destination could be an abstract type. Picking up the best concrete type to use.
 146  144483
       if ((destClass.isAssignableFrom(mappingDestClass) && isAbstract(destClass)) ||
 147  
               (isInterfaceImplementation(destClass, mappingDestClass))) {
 148  30
         if (MappingUtils.getRealClass(srcClass).equals(mappingSrcClass)) {
 149  23
           return map;
 150  
         }
 151  
       }
 152  
 
 153  
     }
 154  471
     return null;
 155  
   }
 156  
 
 157  
   private boolean isInterfaceImplementation(Class<?> type, Class<?> mappingType) {
 158  551185
     return mappingType.isInterface() && mappingType.isAssignableFrom(type);
 159  
   }
 160  
 
 161  
   private static boolean isAbstract(Class<?> destClass) {
 162  81
     return Modifier.isAbstract(destClass.getModifiers());
 163  
   }
 164  
 
 165  
 }