Coverage Report - org.dozer.util.MappingUtils
 
Classes in this File Line Coverage Branch Coverage Complexity
MappingUtils
92%
155/167
85%
82/96
2.897
 
 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.util;
 17  
 
 18  
 import org.apache.commons.lang3.StringUtils;
 19  
 import org.dozer.MappingException;
 20  
 import org.dozer.cache.Cache;
 21  
 import org.dozer.classmap.ClassMap;
 22  
 import org.dozer.classmap.Configuration;
 23  
 import org.dozer.classmap.CopyByReferenceContainer;
 24  
 import org.dozer.classmap.DozerClass;
 25  
 import org.dozer.config.BeanContainer;
 26  
 import org.dozer.converters.CustomConverterContainer;
 27  
 import org.dozer.fieldmap.DozerField;
 28  
 import org.dozer.fieldmap.FieldMap;
 29  
 
 30  
 import java.lang.reflect.Array;
 31  
 import java.util.ArrayList;
 32  
 import java.util.Arrays;
 33  
 import java.util.Collection;
 34  
 import java.util.HashSet;
 35  
 import java.util.Iterator;
 36  
 import java.util.LinkedHashSet;
 37  
 import java.util.LinkedList;
 38  
 import java.util.List;
 39  
 import java.util.Map;
 40  
 import java.util.Set;
 41  
 
 42  
 import static org.dozer.util.DozerConstants.BASE_CLASS;
 43  
 
 44  
 /**
 45  
  * Internal class that provides various mapping utilities used throughout the code base. Only intended for internal use.
 46  
  * 
 47  
  * @author garsombke.franz
 48  
  * @author sullins.ben
 49  
  * @author tierney.matt
 50  
  * 
 51  
  */
 52  
 public final class MappingUtils {
 53  
 
 54  0
   private MappingUtils() {
 55  0
   }
 56  
 
 57  
   public static String getClassNameWithoutPackage(Class<?> clazz) { // TODO Replace with Apache implementation
 58  1
     Package pckage = clazz.getPackage();
 59  1
     int pckageIndex = 0;
 60  1
     if (pckage != null) {
 61  1
       pckageIndex = pckage.getName().length() + 1;
 62  
     }
 63  1
     return clazz.getName().substring(pckageIndex);
 64  
   }
 65  
 
 66  
   public static boolean isSupportedCollection(Class<?> aClass) {
 67  174076
     return CollectionUtils.isCollection(aClass) || CollectionUtils.isArray(aClass);
 68  
   }
 69  
 
 70  
   public static boolean isSupportedMap(Class<?> aClass) {
 71  352174
     return Map.class.isAssignableFrom(aClass);
 72  
   }
 73  
 
 74  
   public static void throwMappingException(Throwable e) throws MappingException {
 75  75
     if (e instanceof MappingException) {
 76  
       // in this case we do not want to re-wrap an existing mapping exception
 77  31
       throw (MappingException) e;
 78  44
     } else if (e instanceof RuntimeException) {
 79  
       // feature request 1561837. Dont wrap any runtime exceptions in a MappingException
 80  10
       throw (RuntimeException) e;
 81  
     } else {
 82  34
       throw new MappingException(e);
 83  
     }
 84  
   }
 85  
 
 86  
   public static void throwMappingException(String msg) throws MappingException {
 87  119
     throw new MappingException(msg);
 88  
   }
 89  
 
 90  
   public static void throwMappingException(String msg, Throwable cause) throws MappingException {
 91  11
     throw new MappingException(msg, cause);
 92  
   }
 93  
 
 94  
   public static boolean isBlankOrNull(String value) {
 95  1690725
     return value == null || value.trim().length() < 1;
 96  
   }
 97  
 
 98  
   public static Throwable getRootCause(Throwable ex) {
 99  1
     Throwable rootCause = ex;
 100  1
     while (rootCause.getCause() != null) {
 101  0
       rootCause = rootCause.getCause();
 102  
     }
 103  1
     return rootCause;
 104  
   }
 105  
 
 106  
   public static String getMappedParentFieldKey(Object destObj, FieldMap destFieldMap) {
 107  443624
     StringBuilder buf = new StringBuilder(100); // TODO Use IdentityHashMap instead of String concatenation
 108  443624
     buf.append(System.identityHashCode(destObj));
 109  443624
     buf.append(destFieldMap.getDestFieldName());
 110  443624
     if ( destFieldMap.getDestFieldKey() != null ) {
 111  123
       buf.append("[").append( destFieldMap.getDestFieldKey() ).append("]");
 112  
     }
 113  443624
     return buf.toString();
 114  
   }
 115  
 
 116  
   public static Class<?> findCustomConverter(Cache converterByDestTypeCache, CustomConverterContainer customConverterContainer,
 117  
       Class<?> srcClass, Class<?> destClass) {
 118  660478
     if (customConverterContainer == null) {
 119  0
       return null;
 120  
     }
 121  
 
 122  660478
     return customConverterContainer.getCustomConverter(srcClass, destClass, converterByDestTypeCache);
 123  
   }
 124  
 
 125  
   public static Class<?> determineCustomConverter(FieldMap fieldMap, Cache converterByDestTypeCache,
 126  
       CustomConverterContainer customConverterContainer, Class<?> srcClass, Class<?> destClass) {
 127  224053
     if (customConverterContainer == null) {
 128  0
       return null;
 129  
     }
 130  
 
 131  
     // This method is messy. Just trying to isolate the junk into this one method instead of spread across the mapping
 132  
     // processor until a better solution can be put into place
 133  
     // For indexed mapping, need to use the actual class at index to determine the custom converter.
 134  224053
     if (fieldMap != null && fieldMap.isDestFieldIndexed()) {
 135  46
       if (destClass.isArray()) {
 136  18
         destClass = destClass.getComponentType();
 137  28
       } else if (destClass.isAssignableFrom(Collection.class) && fieldMap.getDestHintContainer() != null
 138  
           && !fieldMap.getDestHintContainer().hasMoreThanOneHint()) {
 139  
         // use hint when trying to find a custom converter
 140  0
         destClass = fieldMap.getDestHintContainer().getHint();
 141  
       }
 142  
     }
 143  
 
 144  224053
     return findCustomConverter(converterByDestTypeCache, customConverterContainer, srcClass, destClass);
 145  
   }
 146  
 
 147  
   public static void reverseFields(FieldMap source, FieldMap reversed) {
 148  13593
     DozerField destField = source.getSrcFieldCopy();
 149  13593
     DozerField sourceField = source.getDestFieldCopy();
 150  
 
 151  13593
     reversed.setDestField(destField);
 152  13593
     reversed.setSrcField(sourceField);
 153  
 
 154  13593
     reversed.setCustomConverter(source.getCustomConverter());
 155  13593
     reversed.setCustomConverterId(source.getCustomConverterId());
 156  13593
     reversed.setMapId(source.getMapId());
 157  13593
     reversed.setRelationshipType(source.getRelationshipType());
 158  13593
     reversed.setRemoveOrphans(source.isRemoveOrphans());
 159  13593
     reversed.setSrcHintContainer(source.getDestHintContainer());
 160  13593
     reversed.setDestHintContainer(source.getSrcHintContainer());
 161  13593
     reversed.setSrcDeepIndexHintContainer(source.getDestDeepIndexHintContainer());
 162  13593
     reversed.setDestDeepIndexHintContainer(source.getSrcDeepIndexHintContainer());
 163  13593
   }
 164  
 
 165  
   public static void reverseFields(ClassMap source, ClassMap destination) {
 166  
     // reverse the fields
 167  5398
     destination.setSrcClass(new DozerClass(source.getDestClassName(), source.getDestClassToMap(), source.getDestClassBeanFactory(),
 168  
         source.getDestClassBeanFactoryId(), source.getDestClassMapGetMethod(), source.getDestClassMapSetMethod(),
 169  
             source.getDestClass().getCreateMethod(),
 170  
             source.isDestMapNull(), source.isDestMapEmptyString(), source.getDestClass().isAccesible()));
 171  5398
     destination.setDestClass(new DozerClass(source.getSrcClassName(), source.getSrcClassToMap(), source.getSrcClassBeanFactory(),
 172  
         source.getSrcClassBeanFactoryId(), source.getSrcClassMapGetMethod(), source.getSrcClassMapSetMethod(),
 173  
             source.getSrcClass().getCreateMethod(),
 174  
             source.isSrcMapNull(), source.isSrcMapEmptyString(), source.getSrcClass().isAccesible()));
 175  5398
     destination.setType(source.getType());
 176  5398
     destination.setWildcard(source.isWildcard());
 177  5398
     destination.setTrimStrings(source.isTrimStrings());
 178  5398
     destination.setDateFormat(source.getDateFormat());
 179  5398
     destination.setRelationshipType(source.getRelationshipType());
 180  5398
     destination.setStopOnErrors(source.isStopOnErrors());
 181  5398
     destination.setAllowedExceptions(source.getAllowedExceptions());
 182  5398
     destination.setSrcClassCreateMethod(source.getDestClassCreateMethod());
 183  5398
     destination.setDestClassCreateMethod(source.getSrcClassCreateMethod());
 184  5398
     if (StringUtils.isNotEmpty(source.getMapId())) {
 185  666
       destination.setMapId(source.getMapId());
 186  
     }
 187  5398
   }
 188  
 
 189  
   public static Object getIndexedValue(Object collection, int index) {
 190  146
     Object result = null;
 191  146
     if (collection instanceof Object[]) {
 192  26
       Object[] x = (Object[]) collection;
 193  26
       if (index < x.length) {
 194  26
         return x[index];
 195  
       }
 196  0
     } else if (collection instanceof Collection) {
 197  62
       Collection<?> x = (Collection<?>) collection;
 198  62
       if (index < x.size()) {
 199  59
         Iterator<?> iter = x.iterator();
 200  77
         for (int i = 0; i < index; i++) {
 201  18
           iter.next();
 202  
         }
 203  59
         result = iter.next();
 204  
       }
 205  
     }
 206  120
     return result;
 207  
   }
 208  
 
 209  
   public static void applyGlobalCopyByReference(Configuration globalConfig, FieldMap fieldMap, ClassMap classMap) {
 210  38524
     CopyByReferenceContainer copyByReferenceContainer = globalConfig.getCopyByReferences();    
 211  38524
     String destFieldTypeName = null;
 212  38524
     Class<?> clazz = fieldMap.getDestFieldType(classMap.getDestClassToMap());
 213  38524
     if (clazz != null) {
 214  38524
       destFieldTypeName = clazz.getName();
 215  
     }
 216  38524
     if (copyByReferenceContainer.contains(destFieldTypeName) && !fieldMap.isCopyByReferenceOveridden()) {
 217  240
       fieldMap.setCopyByReference(true);
 218  
     }
 219  38524
   }
 220  
 
 221  
   public static Class<?> loadClass(String name) {
 222  23709
     BeanContainer container = BeanContainer.getInstance();
 223  23709
     DozerClassLoader loader = container.getClassLoader();
 224  23709
     return loader.loadClass(name);
 225  
   }
 226  
 
 227  
   public static Class<?> getRealClass(Class<?> clazz) {
 228  400832
     BeanContainer container = BeanContainer.getInstance();
 229  400832
     DozerProxyResolver proxyResolver = container.getProxyResolver();
 230  400832
     return proxyResolver.getRealClass(clazz);
 231  
   }
 232  
 
 233  
   public static <T> T deProxy(T object) {
 234  477303
     BeanContainer container = BeanContainer.getInstance();
 235  477303
     DozerProxyResolver proxyResolver = container.getProxyResolver();
 236  477303
     return proxyResolver.unenhanceObject(object);
 237  
   }
 238  
 
 239  
   public static boolean isProxy(Class<?> clazz) {
 240  18
     BeanContainer container = BeanContainer.getInstance();
 241  18
     DozerProxyResolver proxyResolver = container.getProxyResolver();
 242  18
     return proxyResolver.isProxy(clazz);
 243  
   }
 244  
 
 245  
   public static Object prepareIndexedCollection(Class<?> collectionType, Object existingCollection, Object collectionEntry,
 246  
       int index) {
 247  82
     Object result = null;
 248  82
     if (collectionType.isArray()) {
 249  34
       result = prepareIndexedArray(collectionType, existingCollection, collectionEntry, index);
 250  48
     } else if (Collection.class.isAssignableFrom(collectionType)) {
 251  47
       result = prepareIndexedCollectionType(collectionType, existingCollection, collectionEntry, index);
 252  
     } else {
 253  1
       throwMappingException("Only types java.lang.Object[] and java.util.Collection are supported for indexed properties.");
 254  
     }
 255  
 
 256  81
     return result;
 257  
   }
 258  
 
 259  
   public static boolean isDeepMapping(String mapping) {
 260  959233
     return mapping != null && mapping.contains(DozerConstants.DEEP_FIELD_DELIMITER);
 261  
   }
 262  
 
 263  
   @SuppressWarnings("unchecked")
 264  
   private static <T> T[] prepareIndexedArray(Class<T> collectionType, Object existingCollection, Object collectionEntry, int index) {
 265  
     T[] result;
 266  
 
 267  34
     if (existingCollection == null) {
 268  26
       result = (T[]) Array.newInstance(collectionType.getComponentType(), index + 1);
 269  
     } else {
 270  8
       int originalLenth = ((Object[]) existingCollection).length;
 271  8
       result = (T[]) Array.newInstance(collectionType.getComponentType(), Math.max(index + 1, originalLenth));
 272  8
       System.arraycopy(existingCollection, 0, result, 0, originalLenth);
 273  
     }
 274  34
     result[index] = (T) collectionEntry;
 275  34
     return result;
 276  
   }
 277  
 
 278  
   @SuppressWarnings("unchecked")
 279  
   private static Collection<?> prepareIndexedCollectionType(Class<?> collectionType, Object existingCollection,
 280  
       Object collectionEntry, int index) {
 281  47
     Collection result = null;
 282  
     //Instantiation of the new Collection: can be interface or implementation class
 283  47
     if (collectionType.isInterface()) {
 284  42
       if (collectionType.equals(Set.class)) {
 285  10
         result = new HashSet();
 286  32
       } else if (collectionType.equals(List.class)) {
 287  32
         result = new ArrayList();
 288  
       } else {
 289  0
         throwMappingException("Only interface types java.util.Set and java.util.List are supported for java.util.Collection type indexed properties.");
 290  
       }
 291  
     } else {
 292  
       //It is an implementation class of Collection
 293  
       try {
 294  5
         result = (Collection) collectionType.newInstance();
 295  0
       } catch (InstantiationException e) {
 296  0
         throw new RuntimeException(e);
 297  0
       } catch (IllegalAccessException e) {
 298  0
         throw new RuntimeException(e);
 299  5
       }
 300  
     }
 301  
 
 302  
     //Fill in old values in new Collection
 303  47
     if (existingCollection != null) {
 304  13
       result.addAll((Collection) existingCollection);
 305  
     }
 306  
 
 307  
     //Add the new value:
 308  
     //For an ordered Collection
 309  47
     if (result instanceof List) {
 310  86
       while (result.size() < index + 1) {
 311  49
         result.add(null);
 312  
       }
 313  37
       ((List) result).set(index, collectionEntry);
 314  
     }
 315  
     //for an unordered Collection (index has no use here)
 316  
     else {
 317  10
       result.add(collectionEntry);
 318  
     }
 319  47
     return result;
 320  
   }
 321  
 
 322  
   /**
 323  
    * Used to test if {@code srcFieldClass} is enum.
 324  
    * @param srcFieldClass the source field to be tested.
 325  
    * @return {@code true} if and only if current running JRE is 1.5 or above, and
 326  
    * {@code srcFieldClass} is enum; otherwise return {@code false}.
 327  
    */
 328  
   public static boolean isEnumType(Class<?> srcFieldClass) {
 329  42406
     if (srcFieldClass.isAnonymousClass()) {
 330  
       //If srcFieldClass is anonymous class, replace srcFieldClass with its enclosing class.
 331  
       //This is used to ensure Dozer can get correct Enum type.
 332  15
       srcFieldClass = srcFieldClass.getEnclosingClass();
 333  
     }
 334  42406
     return srcFieldClass.isEnum();
 335  
   }
 336  
 
 337  
   /**
 338  
    * Used to test if both {@code srcFieldClass} and {@code destFieldType} are enum.
 339  
    * @param srcFieldClass the source field to be tested.
 340  
    * @param destFieldType the destination field to be tested.
 341  
    * @return {@code true} if and only if current running JRE is 1.5 or above, and both 
 342  
    * {@code srcFieldClass} and {@code destFieldType} are enum; otherwise return {@code false}.
 343  
    */
 344  
   public static boolean isEnumType(Class<?> srcFieldClass, Class<?> destFieldType) {
 345  40897
     return isEnumType(srcFieldClass) && isEnumType(destFieldType);
 346  
   }
 347  
 
 348  
   public static List<Class<?>> getSuperClassesAndInterfaces(Class<?> srcClass) {
 349  2512
     List<Class<?>> superClasses = new ArrayList<Class<?>>();
 350  2512
     Class<?> realClass = getRealClass(srcClass);
 351  
 
 352  
     // Add all super classes first
 353  2512
     Class<?> superClass = getRealClass(realClass).getSuperclass();
 354  4879
     while (!isBaseClass(superClass)) {
 355  2367
       superClasses.add(superClass);
 356  2367
       superClass = superClass.getSuperclass();
 357  
     }
 358  
 
 359  
     // Now add all interfaces of the passed in class and all it's super classes
 360  
 
 361  
     // Linked hash set so duplicated are not added but insertion order is kept 
 362  2512
     LinkedHashSet<Class<?>> interfaces = new LinkedHashSet<Class<?>>();
 363  
 
 364  2512
     interfaces.addAll(getInterfaceHierarchy(realClass));
 365  2512
     for (Class<?> clazz : superClasses) {
 366  2367
       interfaces.addAll(getInterfaceHierarchy(clazz));
 367  2367
     }
 368  
 
 369  2512
     superClasses.addAll(interfaces);
 370  2512
     return superClasses;
 371  
   }
 372  
 
 373  
   @SuppressWarnings("unchecked")
 374  
   public static List<Class<?>> getInterfaceHierarchy(Class<?> srcClass) {
 375  4887
     final List<Class<?>> result = new LinkedList<Class<?>>();
 376  4887
     Class<?> realClass = getRealClass(srcClass);
 377  
 
 378  4887
     final LinkedList<Class> interfacesToProcess = new LinkedList<Class>();
 379  
 
 380  4887
     Class[] interfaces = realClass.getInterfaces();
 381  
 
 382  4887
     interfacesToProcess.addAll(Arrays.asList(interfaces));
 383  
 
 384  7615
     while (!interfacesToProcess.isEmpty()) {
 385  2728
       Class<?> iface = interfacesToProcess.remove();
 386  2728
       if (!result.contains(iface)) {
 387  2722
         result.add(iface);
 388  3009
         for (Class subiface : iface.getInterfaces()) {
 389  
           // if we haven't processed this interface yet then add it to be processed
 390  287
           if (!result.contains(subiface)) {
 391  269
             interfacesToProcess.add(subiface);
 392  
           }
 393  
         }
 394  
       }
 395  2728
     }
 396  
 
 397  4887
     return result;
 398  
 
 399  
   }
 400  
   
 401  
   private static boolean isBaseClass(Class<?> clazz) {
 402  4879
     return clazz == null || BASE_CLASS.equals(clazz.getName());
 403  
   }
 404  
   
 405  
 }