Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.modelmapper.internal.MappingEngineImpl Maven / Gradle / Ivy
/*
* Copyright 2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.modelmapper.internal;
import java.lang.reflect.Type;
import net.jodah.typetools.TypeResolver.Unknown;
import org.modelmapper.Condition;
import org.modelmapper.ConfigurationException;
import org.modelmapper.Converter;
import org.modelmapper.Provider;
import org.modelmapper.TypeMap;
import org.modelmapper.TypeToken;
import org.modelmapper.internal.converter.ConverterStore;
import org.modelmapper.internal.util.Iterables;
import org.modelmapper.internal.util.Objects;
import org.modelmapper.internal.util.Primitives;
import org.modelmapper.internal.util.Types;
import org.modelmapper.spi.ConstantMapping;
import org.modelmapper.spi.Mapping;
import org.modelmapper.spi.MappingContext;
import org.modelmapper.spi.MappingEngine;
import org.modelmapper.spi.PropertyMapping;
import java.lang.reflect.Constructor;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* MappingEngine implementation that caches ConditionalConverters by source and destination type
* pairs.
*
* @author Jonathan Halterman
*/
public class MappingEngineImpl implements MappingEngine {
/** Cache of conditional converters */
private final Map, Converter, ?>> converterCache = new ConcurrentHashMap, Converter, ?>>();
private final InheritingConfiguration configuration;
private final TypeMapStore typeMapStore;
private final ConverterStore converterStore;
public MappingEngineImpl(InheritingConfiguration configuration) {
this.configuration = configuration;
this.typeMapStore = configuration.typeMapStore;
this.converterStore = configuration.converterStore;
}
/**
* Initial entry point.
*/
public D map(S source, Class sourceType, D destination,
TypeToken destinationTypeToken, String typeMapName) {
MappingContextImpl context = new MappingContextImpl(source, sourceType,
destination, destinationTypeToken.getRawType(), destinationTypeToken.getType(),
typeMapName, this);
D result = null;
try {
result = map(context);
} catch (ConfigurationException e) {
throw e;
} catch (ErrorsException e) {
throw context.errors.toMappingException();
} catch (Throwable t) {
context.errors.errorMapping(sourceType, destinationTypeToken.getType(), t);
}
context.errors.throwMappingExceptionIfErrorsExist();
return result;
}
/**
* Performs mapping using a TypeMap if one exists, else a converter if one applies, else a newly
* created TypeMap. Recursive entry point.
*/
@Override
@SuppressWarnings("unchecked")
public D map(MappingContext context) {
MappingContextImpl contextImpl = (MappingContextImpl) context;
Class destinationType = context.getDestinationType();
// Resolve some circular dependencies
if (!Iterables.isIterable(destinationType)) {
D circularDest = contextImpl.destinationForSource();
if (circularDest != null && circularDest.getClass().isAssignableFrom(contextImpl.getDestinationType()))
return circularDest;
}
D destination = null;
TypeMap typeMap = typeMapStore.get(context.getSourceType(), context.getDestinationType(),
context.getTypeMapName());
if (typeMap != null) {
destination = typeMap(contextImpl, typeMap);
} else {
Converter converter = converterFor(context);
if (converter != null && (context.getDestination() == null || context.getParent() != null))
destination = convert(context, converter);
else if (!Primitives.isPrimitive(context.getSourceType()) && !Primitives.isPrimitive(context.getDestinationType())) {
// Call getOrCreate in case TypeMap was created concurrently
typeMap = typeMapStore.getOrCreate(context.getSource(), context.getSourceType(),
context.getDestinationType(), context.getTypeMapName(), this);
destination = typeMap(contextImpl, typeMap);
} else if (context.getDestinationType().isAssignableFrom(context.getSourceType()))
destination = (D) context.getSource();
}
contextImpl.setDestination(destination, true);
return destination;
}
/**
* Performs a type mapping for the {@code typeMap} and {@code context}.
*/
D typeMap(MappingContextImpl context, TypeMap typeMap) {
if (context.getParent() != null && context.getDestination() == null)
context.setDestination(destinationProperty(context), false);
context.setTypeMap(typeMap);
@SuppressWarnings("unchecked")
Condition condition = (Condition) typeMap.getCondition();
boolean noSkip = condition == null || condition.applies(context);
if (noSkip && typeMap.getConverter() != null)
return convert(context, typeMap.getConverter());
if (context.getDestination() == null && Types.isInstantiable(context.getDestinationType())) {
D destination = createDestination(context);
if (destination == null)
return null;
}
if (noSkip) {
Converter converter = typeMap.getPreConverter();
if (converter != null)
context.setDestination(convert(context, converter), true);
for (Mapping mapping : typeMap.getMappings())
propertyMap(mapping, context);
converter = typeMap.getPostConverter();
if (converter != null)
convert(context, converter);
}
return context.getDestination();
}
@SuppressWarnings("unchecked")
private void propertyMap(Mapping mapping, MappingContextImpl context) {
MappingImpl mappingImpl = (MappingImpl) mapping;
String propertyPath = context.destinationPath + mappingImpl.getPath();
if (context.isShaded(propertyPath))
return;
if (mapping.getCondition() == null && mapping.isSkipped()) // skip()
return;
Object source = resolveSourceValue(context, mapping);
MappingContextImpl propertyContext = propertyContextFor(context, source,
mappingImpl);
Condition condition = (Condition) Objects.firstNonNull(
mapping.getCondition(),
context.getTypeMap().getPropertyCondition(),
configuration.getPropertyCondition());
if (condition != null) {
boolean conditionIsTrue = condition.applies(propertyContext);
if (conditionIsTrue && mapping.isSkipped()) // when(condition).skip()
return;
else if (!conditionIsTrue && !mapping.isSkipped()) { // when(condition)
context.shadePath(propertyPath);
return;
}
}
setDestinationValue(context, propertyContext, mappingImpl);
}
@SuppressWarnings("unchecked")
private Object resolveSourceValue(MappingContextImpl, ?> context, Mapping mapping) {
Object source = context.getSource();
if (mapping instanceof PropertyMappingImpl) {
for (Accessor accessor : (List) ((PropertyMapping) mapping).getSourceProperties()) {
context.setParentSource(source);
source = accessor.getValue(source);
if (source == null)
return null;
if (!Iterables.isIterable(source.getClass())) {
Object circularDest = context.sourceToDestination.get(source);
if (circularDest != null)
context.intermediateDestinations.add(circularDest);
}
}
} else if (mapping instanceof ConstantMapping)
source = ((ConstantMapping) mapping).getConstant();
return source;
}
/**
* Sets a mapped or converted destination value in the last mapped mutator for the given
* {@code mapping}. The final destination value is resolved by walking the {@code mapping}'s
* mutator chain and obtaining each destination value in the chain either from the cache, from a
* corresponding accessor, from a provider, or by instantiation, in that order.
*/
@SuppressWarnings("unchecked")
private void setDestinationValue(MappingContextImpl context,
MappingContextImpl propertyContext, MappingImpl mapping) {
String destPath = context.destinationPath + mapping.getPath();
Converter converter = (Converter) Objects.firstNonNull(
mapping.getConverter(),
context.getTypeMap().getPropertyConverter());
if (converter != null)
context.shadePath(destPath);
Object destination = propertyContext.getParentDestination();
if (destination == null)
return;
Mutator mutator = (Mutator) mapping.getLastDestinationProperty();
Accessor accessor = PropertyInfoRegistry.accessorFor(mutator.getInitialType(), mutator.getName(), configuration);
Object destinationValue = propertyContext.createDestinationViaProvider();
if (destinationValue == null && propertyContext.isProvidedDestination() && accessor != null) {
destinationValue = accessor.getValue(destination);
propertyContext.setDestination(destinationValue, false);
}
if (converter != null)
destinationValue = convert(propertyContext, converter);
else if (propertyContext.getSource() != null)
destinationValue = map(propertyContext);
else {
converter = converterFor(propertyContext);
if (converter != null)
destinationValue = convert(propertyContext, converter);
}
context.destinationCache.put(destPath, destinationValue);
if (destinationValue != null || !configuration.isSkipNullEnabled())
mutator.setValue(destination,
destinationValue == null ? Primitives.defaultValue(mutator.getType())
: destinationValue);
if (destinationValue == null)
context.shadePath(propertyContext.destinationPath);
}
/**
* Returns a property context.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private MappingContextImpl propertyContextFor(MappingContextImpl, ?> context,
Object source, MappingImpl mapping) {
Class> sourceType = mapping.getSourceType();
if (source != null)
sourceType = Types.deProxy(source.getClass());
boolean cyclic = mapping instanceof PropertyMapping && ((PropertyMappingImpl) mapping).cyclic;
Class destinationType = (Class) mapping.getLastDestinationProperty().getType();
Type genericDestinationType = context.genericDestinationPropertyType(mapping.getLastDestinationProperty().getGenericType());
return new MappingContextImpl(context, source, sourceType, null, destinationType, genericDestinationType,
mapping, !cyclic);
}
private D destinationProperty(MappingContextImpl context) {
if (!context.isProvidedDestination() || context.getMapping() == null)
return null;
Object intermediateDest = context.getParent().getDestination();
@SuppressWarnings("unchecked")
List mutatorChain = (List) context.getMapping().getDestinationProperties();
for (Mutator mutator : mutatorChain) {
if (intermediateDest == null)
break;
Accessor accessor = TypeInfoRegistry.typeInfoFor(intermediateDest.getClass(),
configuration)
.getAccessors()
.get(mutator.getName());
if (accessor != null)
intermediateDest = accessor.getValue(intermediateDest);
}
@SuppressWarnings("unchecked")
D destinationProperty = (D) intermediateDest;
return destinationProperty;
}
/**
* Performs a mapping using a Converter.
*/
private D convert(MappingContext context, Converter converter) {
try {
return converter.convert(context);
} catch (ErrorsException e) {
throw e;
} catch (Exception e) {
((MappingContextImpl) context).errors.errorConverting(converter,
context.getSourceType(), context.getDestinationType(), e);
return null;
}
}
/**
* Retrieves a converter from the store or from the cache.
*/
@SuppressWarnings("unchecked")
private Converter converterFor(MappingContext context) {
TypePair, ?> typePair = TypePair.of(context.getSourceType(), context.getDestinationType(),
context.getTypeMapName());
Converter converter = (Converter) converterCache.get(typePair);
if (converter == null) {
converter = converterStore.getFirstSupported(context.getSourceType(),
context.getDestinationType());
if (converter != null)
converterCache.put(typePair, converter);
}
return converter;
}
private T instantiate(Class type, Errors errors) {
try {
Constructor constructor = type.getDeclaredConstructor();
if (!constructor.isAccessible())
constructor.setAccessible(true);
return constructor.newInstance();
} catch (Exception e) {
errors.errorInstantiatingDestination(type, e);
return null;
}
}
@Override
public D createDestination(MappingContext context) {
MappingContextImpl contextImpl = (MappingContextImpl) context;
D destination = contextImpl.createDestinationViaProvider();
if (destination == null)
destination = instantiate(context.getDestinationType(), contextImpl.errors);
contextImpl.setDestination(destination, true);
return destination;
}
InheritingConfiguration getConfiguration() {
return configuration;
}
@SuppressWarnings("unchecked")
D createDestinationViaGlobalProvider(S source, Class requestedType,
Errors errors) {
D destination = null;
Provider provider = (Provider) configuration.getProvider();
if (provider != null) {
destination = provider.get(new ProvisionRequestImpl(source, requestedType));
validateDestination(requestedType, destination, errors);
}
if (destination == null)
destination = instantiate(requestedType, errors);
return destination;
}
void validateDestination(Class> destinationType, Object destination, Errors errors) {
if (destination != null && !destinationType.isAssignableFrom(destination.getClass()))
errors.invalidProvidedDestinationInstance(destination, destinationType);
}
}