1
16 package org.modelmapper.internal;
17
18 import org.modelmapper.Condition;
19 import org.modelmapper.Converter;
20 import org.modelmapper.ExpressionMap;
21 import org.modelmapper.PropertyMap;
22 import org.modelmapper.Provider;
23 import org.modelmapper.TypeMap;
24 import org.modelmapper.internal.util.Assert;
25 import org.modelmapper.internal.util.Objects;
26 import org.modelmapper.internal.util.Types;
27 import org.modelmapper.spi.DestinationSetter;
28 import org.modelmapper.spi.Mapping;
29 import org.modelmapper.spi.PropertyInfo;
30 import org.modelmapper.spi.SourceGetter;
31 import org.modelmapper.spi.TypeSafeSourceGetter;
32
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.HashSet;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Set;
40 import java.util.Stack;
41 import java.util.TreeMap;
42
43
48 class TypeMapImpl<S, D> implements TypeMap<S, D> {
49 private final Class<S> sourceType;
50 private final Class<D> destinationType;
51 private final String name;
52 final InheritingConfiguration configuration;
53 private final MappingEngineImpl engine;
54
55 private final Map<String, InternalMapping> mappings = new TreeMap<String, InternalMapping>();
56 private Converter<S, D> converter;
57 private Converter<S, D> preConverter;
58 private Converter<S, D> postConverter;
59 private Condition<?, ?> condition;
60 private Provider<D> provider;
61 private Converter<?, ?> propertyConverter;
62 private Condition<?, ?> propertyCondition;
63 private Provider<?> propertyProvider;
64
65 TypeMapImpl(Class<S> sourceType, Class<D> destinationType, String name,
66 InheritingConfiguration configuration, MappingEngineImpl engine) {
67 this.sourceType = sourceType;
68 this.destinationType = destinationType;
69 this.name = name;
70 this.configuration = configuration;
71 this.engine = engine;
72 }
73
74 private TypeMapImpl(TypeMapImpl<? super S, ? super D> baseTypeMap, Class<S> sourceTYpe, Class<D> destinationType) {
75 this.sourceType = sourceTYpe;
76 this.destinationType = destinationType;
77 this.name = baseTypeMap.name;
78 this.configuration = baseTypeMap.configuration;
79 this.engine = baseTypeMap.engine;
80
81 synchronized (baseTypeMap.mappings) {
82 mappings.putAll(baseTypeMap.mappings);
83 }
84 }
85
86 @Override
87 public TypeMap<S, D> addMappings(PropertyMap<S, D> propertyMap) {
88 if (sourceType.isEnum() || destinationType.isEnum())
89 throw new Errors().mappingForEnum().toConfigurationException();
90
91 synchronized (mappings) {
92 for (InternalMapping mapping : ExplicitMappingBuilder.build(sourceType,
93 destinationType, configuration, propertyMap)) {
94 InternalMapping existingMapping = addMapping(mapping);
95 if (existingMapping != null && existingMapping.isExplicit())
96 throw new Errors().duplicateMapping(mapping.getLastDestinationProperty())
97 .toConfigurationException();
98 }
99 }
100 return this;
101 }
102
103 @Override
104 public Condition<?, ?> getCondition() {
105 return condition;
106 }
107
108 @Override
109 public Converter<S, D> getConverter() {
110 return converter;
111 }
112
113 @Override
114 public Class<D> getDestinationType() {
115 return destinationType;
116 }
117
118 @Override
119 public List<Mapping> getMappings() {
120 synchronized (mappings) {
121 return new ArrayList<Mapping>(mappings.values());
122 }
123 }
124
125 @Override
126 public String getName() {
127 return name;
128 }
129
130 @Override
131 public Converter<S, D> getPostConverter() {
132 return postConverter;
133 }
134
135 @Override
136 public Converter<S, D> getPreConverter() {
137 return preConverter;
138 }
139
140 @Override
141 public Condition<?, ?> getPropertyCondition() {
142 return propertyCondition;
143 }
144
145 @Override
146 public Converter<?, ?> getPropertyConverter() {
147 return propertyConverter;
148 }
149
150 @Override
151 public Provider<?> getPropertyProvider() {
152 return propertyProvider;
153 }
154
155 @Override
156 public Provider<D> getProvider() {
157 return provider;
158 }
159
160 @Override
161 public Class<S> getSourceType() {
162 return sourceType;
163 }
164
165 @Override
166 public List<PropertyInfo> getUnmappedProperties() {
167 PathProperties pathProperties = getDestinationProperties();
168
169 synchronized (mappings) {
170 for (Map.Entry<String, InternalMapping> entry : mappings.entrySet()) {
171 pathProperties.matchAndRemove(entry.getKey());
172 }
173 }
174
175 return pathProperties.get();
176 }
177
178 @Override
179 public D map(S source) {
180 Class<S> sourceType = Types.deProxy(source.getClass());
181 MappingContextImpl<S, D> context = new MappingContextImpl<S, D>(source, sourceType, null,
182 destinationType, null, name, engine);
183 D result = null;
184
185 try {
186 result = engine.typeMap(context, this);
187 } catch (Throwable t) {
188 context.errors.errorMapping(sourceType, destinationType, t);
189 }
190
191 context.errors.throwMappingExceptionIfErrorsExist();
192 return result;
193 }
194
195 @Override
196 public void map(S source, D destination) {
197 Class<S> sourceType = Types.deProxy(source.getClass());
198 MappingContextImpl<S, D> context = new MappingContextImpl<S, D>(source, sourceType,
199 destination, destinationType, null, name, engine);
200
201 try {
202 engine.typeMap(context, this);
203 } catch (Throwable t) {
204 context.errors.errorMapping(sourceType, destinationType, t);
205 }
206
207 context.errors.throwMappingExceptionIfErrorsExist();
208 }
209
210 @Override
211 public TypeMap<S, D> setCondition(Condition<?, ?> condition) {
212 this.condition = Assert.notNull(condition, "condition");
213 return this;
214 }
215
216 @Override
217 public TypeMap<S, D> setConverter(Converter<S, D> converter) {
218 this.converter = Assert.notNull(converter, "converter");
219 return this;
220 }
221
222 @Override
223 public TypeMap<S, D> setPostConverter(Converter<S, D> converter) {
224 this.postConverter = Assert.notNull(converter, "converter");
225 return this;
226 }
227
228 @Override
229 public TypeMap<S, D> setPreConverter(Converter<S, D> converter) {
230 this.preConverter = Assert.notNull(converter, "converter");
231 return this;
232 }
233
234 @Override
235 public TypeMap<S, D> setPropertyCondition(Condition<?, ?> condition) {
236 propertyCondition = Assert.notNull(condition, "condition");
237 return this;
238 }
239
240 @Override
241 public TypeMap<S, D> setPropertyConverter(Converter<?, ?> converter) {
242 propertyConverter = Assert.notNull(converter, "converter");
243 return this;
244 }
245
246 @Override
247 public TypeMap<S, D> setPropertyProvider(Provider<?> provider) {
248 propertyProvider = Assert.notNull(provider, "provider");
249 return this;
250 }
251
252 @Override
253 public TypeMap<S, D> setProvider(Provider<D> provider) {
254 this.provider = Assert.notNull(provider, "provider");
255 return this;
256 }
257
258 @Override
259 public <V> TypeMap<S, D> addMapping(SourceGetter<S> sourceGetter, DestinationSetter<D, V> destinationSetter) {
260 new ReferenceMapExpressionImpl<S, D>(this).map(sourceGetter, destinationSetter);
261 return this;
262 }
263
264 @Override
265 public TypeMap<S, D> addMappings(ExpressionMap<S, D> mapper) {
266 mapper.configure(new ConfigurableConditionExpressionImpl<S, D>(this));
267 return this;
268 }
269
270 @Override
271 public String toString() {
272 StringBuilder b = new StringBuilder();
273 b.append("TypeMap[")
274 .append(sourceType.getSimpleName())
275 .append(" -> ")
276 .append(destinationType.getSimpleName());
277 if (name != null)
278 b.append(' ').append(name);
279 return b.append(']').toString();
280 }
281
282 @Override
283 public void validate() {
284 if (converter != null || preConverter != null || postConverter != null)
285 return;
286
287 Errors errors = new Errors();
288 List<PropertyInfo> unmappedProperties = getUnmappedProperties();
289 if (!unmappedProperties.isEmpty())
290 errors.errorUnmappedProperties(this, unmappedProperties);
291
292 errors.throwValidationExceptionIfErrorsExist();
293 }
294
295 @Override
296 public <DS extends S, DD extends D> TypeMap<S, D> include(Class<DS> sourceType, Class<DD> destinationType) {
297 TypeMapImpl<DS, DD> derivedTypeMap = new TypeMapImpl<DS, DD>(this, sourceType, destinationType);
298 configuration.typeMapStore.put(derivedTypeMap);
299 return this;
300 }
301
302 @Override
303 public TypeMap<S, D> include(Class<? super D> baseDestinationType) {
304 if (provider == null)
305 provider = new Provider<D>() {
306 @Override
307 public D get(ProvisionRequest<D> request) {
308 return Objects.instantiate(destinationType);
309 }
310 };
311
312 configuration.typeMapStore.put(sourceType, baseDestinationType, this);
313 return this;
314 }
315
316 @Override
317 public TypeMap<S, D> includeBase(Class<? super S> sourceType, Class<? super D> destinationType) {
318 @SuppressWarnings("unchecked")
319 TypeMapImpl<? super S, ? super D> baseTypeMap = (TypeMapImpl<? super S, ? super D>)
320 configuration.typeMapStore.get(sourceType, destinationType, name);
321
322 Assert.notNull(baseTypeMap, "Cannot find base TypeMap");
323
324 synchronized (baseTypeMap.mappings) {
325 for (Map.Entry<String, InternalMapping> entry : baseTypeMap.mappings.entrySet()) {
326 addMapping(entry.getValue());
327 }
328 }
329
330 return this;
331 }
332
333 @Override
334 public <P> TypeMap<S, D> include(TypeSafeSourceGetter<S, P> sourceGetter, Class<P> propertyType) {
335 @SuppressWarnings("unchecked")
336 TypeMapImpl<? super S, ? super D> childTypeMap = (TypeMapImpl<? super S, ? super D>)
337 configuration.typeMapStore.get(propertyType, destinationType, name);
338 Assert.notNull(childTypeMap, "Cannot find child TypeMap");
339
340 List<Accessor> accessors = PropertyReferenceCollector.collect(this, sourceGetter);
341 for (Mapping mapping : childTypeMap.getMappings()) {
342 InternalMapping internalMapping = (InternalMapping) mapping;
343 addMapping(internalMapping.createMergedCopy(accessors, Collections.<PropertyInfo>emptyList()));
344 }
345 return this;
346 }
347
348 @Override
349 public TypeMap<S, D> implicitMappings() {
350 ImplicitMappingBuilder.build(null, this, configuration.typeMapStore, configuration.converterStore);
351 return this;
352 }
353
354 void addMappingIfAbsent(InternalMapping mapping) {
355 synchronized (mappings) {
356 if (!mappings.containsKey(mapping.getPath()))
357 mappings.put(mapping.getPath(), mapping);
358 }
359 }
360
361 InternalMapping addMapping(InternalMapping mapping) {
362 synchronized (mappings) {
363 return mappings.put(mapping.getPath(), mapping);
364 }
365 }
366
367
371 boolean isSkipped(String path) {
372 Mapping mapping = mappings.get(path);
373 return mapping != null && mapping.isSkipped();
374 }
375
376
380 Mapping mappingFor(String path) {
381 return mappings.get(path);
382 }
383
384 boolean isFullMatching() {
385 return getUnmappedProperties().isEmpty()
386 || configuration.valueAccessStore.getFirstSupportedReader(sourceType) == null;
387 }
388
389 private PathProperties getDestinationProperties() {
390 PathProperties pathProperties = new PathProperties();
391 Set<Class<?>> classes = new HashSet<Class<?>>();
392
393 Stack<Property> propertyStack = new Stack<Property>();
394 propertyStack.push(new Property("", TypeInfoRegistry.typeInfoFor(destinationType, configuration)));
395
396 while (!propertyStack.isEmpty()) {
397 Property property = propertyStack.pop();
398 classes.add(property.typeInfo.getType());
399 for (Map.Entry<String, Mutator> entry : property.typeInfo.getMutators().entrySet()) {
400 if (entry.getValue() instanceof PropertyInfoImpl.FieldPropertyInfo
401 && !configuration.isFieldMatchingEnabled()) {
402 continue;
403 }
404
405 String path = property.prefix + entry.getKey() + ".";
406 Mutator mutator = entry.getValue();
407 pathProperties.pathProperties.add(new PathProperty(path, mutator));
408
409 if (!classes.contains(mutator.getType())
410 && Types.mightContainsProperties(mutator.getType()))
411 propertyStack.push(new Property(path, TypeInfoRegistry.typeInfoFor(mutator.getType(), configuration)));
412 }
413 }
414 return pathProperties;
415 }
416
417 private static final class Property {
418 String prefix;
419 TypeInfo<?> typeInfo;
420
421 public Property(String prefix, TypeInfo<?> typeInfo) {
422 this.prefix = prefix;
423 this.typeInfo = typeInfo;
424 }
425 }
426
427 private static final class PathProperties {
428 List<PathProperty> pathProperties = new ArrayList<PathProperty>();
429
430 private void matchAndRemove(String path) {
431 int startIndex = 0;
432 int endIndex;
433 while ((endIndex = path.indexOf(".", startIndex)) != -1) {
434 String currentPath = path.substring(0, endIndex + 1);
435
436 Iterator<PathProperty> iterator = pathProperties.iterator();
437 while (iterator.hasNext())
438 if (iterator.next().path.equals(currentPath))
439 iterator.remove();
440
441 startIndex = endIndex + 1;
442 }
443
444 Iterator<PathProperty> iterator = pathProperties.iterator();
445 while (iterator.hasNext())
446 if (iterator.next().path.startsWith(path))
447 iterator.remove();
448 }
449
450 public List<PropertyInfo> get() {
451 List<PropertyInfo> mutators = new ArrayList<PropertyInfo>(pathProperties.size());
452 for (PathProperty pathProperty : pathProperties)
453 mutators.add(pathProperty.mutator);
454 return mutators;
455 }
456 }
457
458 private static final class PathProperty {
459 String path;
460 Mutator mutator;
461
462 private PathProperty(String path, Mutator mutator) {
463 this.path = path;
464 this.mutator = mutator;
465 }
466 }
467 }
468