1 /*
2  * Copyright 2011 the original author or authors.
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.modelmapper.internal.converter;
17
18 import java.lang.reflect.ParameterizedType;
19 import java.lang.reflect.Type;
20 import java.util.HashMap;
21 import java.util.Map;
22 import java.util.Map.Entry;
23 import java.util.SortedMap;
24 import java.util.TreeMap;
25
26 import net.jodah.typetools.TypeResolver;
27 import net.jodah.typetools.TypeResolver.Unknown;
28
29 import org.modelmapper.spi.ConditionalConverter;
30 import org.modelmapper.spi.Mapping;
31 import org.modelmapper.spi.MappingContext;
32 import org.modelmapper.spi.PropertyInfo;
33 import org.modelmapper.spi.PropertyMapping;
34
35 /**
36  * Converts {@link Map} instances to each other.
37  *
38  * @author Jonathan Halterman
39  */

40 class MapConverter implements ConditionalConverter<Map<?, ?>, Map<Object, Object>> {
41   public Map<Object, Object> convert(MappingContext<Map<?, ?>, Map<Object, Object>> context) {
42     Map<?, ?> source = context.getSource();
43     if (source == null)
44       return null;
45
46     Map<Object, Object> destination = context.getDestination() == null ? createDestination(context)
47         : context.getDestination();
48     Mapping mapping = context.getMapping();
49
50     Type keyElementType = Object.class;
51     Type valueElementType = Object.class;
52     if (mapping instanceof PropertyMapping) {
53       PropertyInfo destInfo = mapping.getLastDestinationProperty();
54       Class<?>[] elementTypes = TypeResolver.resolveRawArguments(destInfo.getGenericType(),
55           Map.class);
56       if (elementTypes != null && elementTypes.length == 2) {
57         keyElementType = elementTypes[0] == Unknown.class ? Object.class : elementTypes[0];
58         valueElementType = elementTypes[1] == Unknown.class ? Object.class : elementTypes[1];
59       }
60     } else if (context.getGenericDestinationType() instanceof ParameterizedType) {
61       Type[] elementTypes = ((ParameterizedType) context.getGenericDestinationType()).getActualTypeArguments();
62       keyElementType = elementTypes[0];
63       valueElementType = elementTypes[1];
64     }
65
66     for (Entry<?, ?> entry : source.entrySet()) {
67       Object key = null;
68       if (entry.getKey() != null) {
69         MappingContext<?, ?> keyContext = context.create(entry.getKey(), keyElementType);
70         key = context.getMappingEngine().map(keyContext);
71       }
72
73       Object value = null;
74       if (entry.getValue() != null) {
75         MappingContext<?, ?> valueContext = context.create(entry.getValue(), valueElementType);
76         value = context.getMappingEngine().map(valueContext);
77       }
78
79       destination.put(key, value);
80     }
81
82     return destination;
83   }
84
85   public MatchResult match(Class<?> sourceType, Class<?> destinationType) {
86     return Map.class.isAssignableFrom(sourceType) && Map.class.isAssignableFrom(destinationType) ? MatchResult.FULL
87         : MatchResult.NONE;
88   }
89
90   protected Map<Object, Object> createDestination(
91       MappingContext<Map<?, ?>, Map<Object, Object>> context) {
92     if (!context.getDestinationType().isInterface())
93       return context.getMappingEngine().createDestination(context);
94     if (SortedMap.class.isAssignableFrom(context.getDestinationType()))
95       return new TreeMap<Object, Object>();
96     return new HashMap<Object, Object>();
97   }
98 }
99