org.modelmapper.ModelMapper 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;
import net.jodah.typetools.TypeResolver;
import java.lang.reflect.Type;
import java.util.Collection;
import org.modelmapper.config.Configuration;
import org.modelmapper.internal.Errors;
import org.modelmapper.internal.InheritingConfiguration;
import org.modelmapper.internal.MappingEngineImpl;
import org.modelmapper.internal.util.Assert;
import org.modelmapper.internal.util.Types;
/**
* ModelMapper - Performs object mapping, maintains {@link Configuration} and stores {@link TypeMap
* TypeMaps}.
*
*
* - To perform object mapping use {@link #map(Object, Class) map}.
* - To configure the mapping of one type to another use {@link #createTypeMap(Class, Class)
* createTypeMap}.
* - To add mappings for specific properties use {@link #addMappings(PropertyMap) addMappings}
* supplying a {@link PropertyMap}.
* - To configure ModelMapper use {@link #getConfiguration}.
*
- To validate mappings use {@link #validate}.
*
*
* @author Jonathan Halterman
*/
public class ModelMapper {
private final InheritingConfiguration config;
private final MappingEngineImpl engine;
/**
* Creates a new ModelMapper.
*/
public ModelMapper() {
config = new InheritingConfiguration();
engine = new MappingEngineImpl(config);
}
/**
* Registers the {@code converter} to use when mapping instances of types {@code S} to {@code D}.
* The {@code converter} will be {@link TypeMap#setConverter(Converter) set} against TypeMap
* corresponding to the {@code converter}'s type arguments {@code S} and {@code D}.
*
* @param source type
* @param destination type
* @param converter to register
* @throws IllegalArgumentException if {@code converter} is null or if type arguments {@code S}
* and {@code D} are not declared for the {@code converter}
* @see TypeMap#setConverter(Converter)
*/
@SuppressWarnings("unchecked")
public void addConverter(Converter converter) {
Assert.notNull(converter, "converter");
Class>[] typeArguments = TypeResolver.resolveRawArguments(Converter.class, converter.getClass());
Assert.notNull(typeArguments, "Must declare source type argument and destination type argument for converter");
config.typeMapStore.getOrCreate(null, (Class) typeArguments[0],
(Class) typeArguments[1], null, null, converter, engine);
}
/**
* Registers the {@code converter} to use when mapping instances of types {@code S} to {@code D}.
* The {@code converter} will be {@link TypeMap#setConverter(Converter) set} against TypeMap
* corresponding to the {@code converter}'s type arguments {@code S} and {@code D}.
*
* @param source type
* @param destination type
* @param converter to register
* @throws IllegalArgumentException if {@code converter} is null or if type arguments {@code S}
* and {@code D} are not declared for the {@code converter}
* @see TypeMap#setConverter(Converter)
*/
@SuppressWarnings("unchecked")
public void addConverter(Converter converter, Class sourceType, Class destinationType) {
Assert.notNull(converter, "converter");
config.typeMapStore.getOrCreate(null, sourceType,
destinationType, null, null, converter, engine);
}
/**
* Adds mappings from the {@code propertyMap} into the TypeMap corresponding to source type
* {@code S} and destination type {@code D}. Explicit mappings defined in the {@code propertyMap}
* will override any implicit mappings for the same properties.
*
* @param source type
* @param destination type
* @param propertyMap from which mappings should be loaded
* @return TypeMap corresponding to the {@code propertyMap}
* @throws IllegalArgumentException if {@code propertyMap} is null
* @throws ConfigurationException if a configuration error occurs while adding mappings for the
* {@code propertyMap}
*/
public TypeMap addMappings(PropertyMap propertyMap) {
Assert.notNull(propertyMap, "propertyMap");
return config.typeMapStore.getOrCreate(null, propertyMap.sourceType,
propertyMap.destinationType, null, propertyMap, null, engine);
}
/**
* Creates a TypeMap for the {@code sourceType} and {@code destinationType} using the
* ModelMapper's configuration.
*
* @param source type
* @param destination type
* @param sourceType
* @param destinationType
* @throws IllegalArgumentException if {@code sourceType} or {@code destinationType} are null
* @throws IllegalStateException if a TypeMap already exists for {@code sourceType} and
* {@code destinationType}
* @throws ConfigurationException if the ModelMapper cannot create the TypeMap
* @see #getTypeMap(Class, Class)
*/
public TypeMap createTypeMap(Class sourceType, Class destinationType) {
return this.createTypeMap(sourceType, destinationType, config);
}
/**
* Creates a TypeMap for the {@code sourceType} and {@code destinationType} using the
* {@code configuration}.
*
* @param source type
* @param destination type
* @param sourceType
* @param destinationType
* @param configuration to apply to TypeMap
* @throws IllegalArgumentException if {@code sourceType}, {@code destinationType} or
* {@code configuration} are null
* @throws IllegalStateException if a TypeMap already exists for {@code sourceType} and
* {@code destinationType}
* @throws ConfigurationException if the ModelMapper cannot create the TypeMap
* @see #getTypeMap(Class, Class)
*/
public TypeMap createTypeMap(Class sourceType, Class destinationType,
Configuration configuration) {
Assert.notNull(sourceType, "sourceType");
Assert.notNull(destinationType, "destinationType");
Assert.notNull(configuration, "configuration");
return this.createTypeMapInternal(null, sourceType, destinationType, null, configuration);
}
/**
* Creates a TypeMap for the {@code sourceType} and {@code destinationType} identified by the
* {@code typeMapName} using the ModelMapper's configuration.
*
* @param source type
* @param destination type
* @param sourceType
* @param destinationType
* @param typeMapName
* @throws IllegalArgumentException if {@code sourceType}, {@code destinationType} or
* {@code typeMapName} are null
* @throws IllegalStateException if a TypeMap already exists for {@code sourceType},
* {@code destinationType} and {@code typeMapName}
* @throws ConfigurationException if the ModelMapper cannot create the TypeMap
* @see #getTypeMap(Class, Class, String)
*/
public TypeMap createTypeMap(Class sourceType, Class destinationType,
String typeMapName) {
return this.createTypeMap(sourceType, destinationType, typeMapName, config);
}
/**
* Creates a TypeMap for the {@code sourceType} and {@code destinationType} identified by the
* {@code typeMapName} using the {@code configuration}.
*
* @param source type
* @param destination type
* @param sourceType
* @param destinationType
* @param typeMapName
* @param configuration to apply to TypeMap
* @throws IllegalArgumentException if {@code sourceType}, {@code destinationType},
* {@code typeMapName} or {@code configuration} are null
* @throws IllegalStateException if a TypeMap already exists for {@code sourceType},
* {@code destinationType} and {@code typeMapName}
* @throws ConfigurationException if the ModelMapper cannot create the TypeMap
* @see #getTypeMap(Class, Class, String)
*/
public TypeMap createTypeMap(Class sourceType, Class destinationType,
String typeMapName, Configuration configuration) {
Assert.notNull(sourceType, "sourceType");
Assert.notNull(destinationType, "destinationType");
Assert.notNull(typeMapName, "typeMapName");
Assert.notNull(configuration, "configuration");
return createTypeMapInternal(null, sourceType, destinationType, typeMapName, configuration);
}
/**
* Creates a TypeMap for the {@code source}'s type and {@code destinationType} using the
* ModelMapper's configuration. Useful for creating TypeMaps for generic source data structures.
*
* @param source type
* @param destination type
* @param source
* @param destinationType
* @throws IllegalArgumentException if {@code source} or {@code destinationType} are null
* @throws IllegalStateException if a TypeMap already exists for {@code source}'s type and
* {@code destinationType}
* @throws ConfigurationException if the ModelMapper cannot create the TypeMap
* @see #getTypeMap(Class, Class)
*/
public TypeMap createTypeMap(S source, Class destinationType) {
return this.createTypeMap(source, destinationType, config);
}
/**
* Creates a TypeMap for the {@code source}'s type and {@code destinationType} using the
* {@code configuration}. Useful for creating TypeMaps for generic source data structures.
*
* @param source type
* @param destination type
* @param source
* @param destinationType
* @param configuration to apply to TypeMap
* @throws IllegalArgumentException if {@code source}, {@code destinationType} or
* {@code configuration} are null
* @throws IllegalStateException if a TypeMap already exists for {@code source}'s type and
* {@code destinationType}
* @throws ConfigurationException if the ModelMapper cannot create the TypeMap
* @see #getTypeMap(Class, Class)
*/
public TypeMap createTypeMap(S source, Class destinationType,
Configuration configuration) {
Assert.notNull(source, "source");
Assert.notNull(destinationType, "destinationType");
Assert.notNull(configuration, "configuration");
return this.createTypeMapInternal(source, null, destinationType, null, configuration);
}
/**
* Creates a TypeMap for the {@code source}'s type and {@code destinationType} identified by the
* {@code typeMapName} using the ModelMapper's configuration. Useful for creating TypeMaps for
* generic source data structures.
*
* @param source type
* @param destination type
* @param source
* @param destinationType
* @param typeMapName
* @throws IllegalArgumentException if {@code source}, {@code destinationType} or
* {@code typeMapName} are null
* @throws IllegalStateException if a TypeMap already exists for {@code source}'s type,
* {@code destinationType} and {@code typeMapName}
* @throws ConfigurationException if the ModelMapper cannot create the TypeMap
* @see #getTypeMap(Class, Class, String)
*/
public TypeMap createTypeMap(S source, Class destinationType, String typeMapName) {
return this.createTypeMap(source, destinationType, typeMapName, config);
}
/**
* Creates a TypeMap for the {@code source}'s type and {@code destinationType} identified by the
* {@code typeMapName} using the {@code configuration}. Useful for creating TypeMaps for generic
* source data structures.
*
* @param source type
* @param destination type
* @param source
* @param destinationType
* @param typeMapName
* @param configuration to apply to TypeMap
* @throws IllegalArgumentException if {@code source}, {@code destinationType},
* {@code typeMapName} or {@code configuration} are null
* @throws IllegalStateException if a TypeMap already exists for {@code source}'s type,
* {@code destinationType} and {@code typeMapName}
* @throws ConfigurationException if the ModelMapper cannot create the TypeMap
* @see #getTypeMap(Class, Class, String)
*/
public TypeMap createTypeMap(S source, Class destinationType, String typeMapName,
Configuration configuration) {
Assert.notNull(source, "source");
Assert.notNull(destinationType, "destinationType");
Assert.notNull(typeMapName, "typeMapName");
Assert.notNull(configuration, "configuration");
return createTypeMapInternal(source, null, destinationType, typeMapName, configuration);
}
/**
* Returns the ModelMapper's configuration.
*/
public Configuration getConfiguration() {
return config;
}
/**
* Returns the TypeMap for the {@code sourceType} and {@code destinationType}, else returns
* {@code null} if none exists.
*
* @param source type
* @param destination type
* @throws IllegalArgumentException is {@code sourceType} or {@code destinationType} are null
* @see #createTypeMap(Class, Class)
*/
public TypeMap getTypeMap(Class sourceType, Class destinationType) {
Assert.notNull(sourceType, "sourceType");
Assert.notNull(destinationType, "destinationType");
return config.typeMapStore.get(sourceType, destinationType, null);
}
/**
* Returns the TypeMap for the {@code sourceType}, {@code destinationType} and {@code typeMapName}
* , else returns {@code null} if none exists.
*
* @param source type
* @param destination type
* @throws IllegalArgumentException is {@code sourceType}, {@code destinationType} or
* {@code typeMapName} are null
* @see #createTypeMap(Class, Class, String)
*/
public TypeMap getTypeMap(Class sourceType, Class destinationType,
String typeMapName) {
Assert.notNull(sourceType, "sourceType");
Assert.notNull(destinationType, "destinationType");
Assert.notNull(typeMapName, "typeMapName");
return config.typeMapStore.get(sourceType, destinationType, typeMapName);
}
/**
* Returns the TypeMap for the {@code sourceType}, {@code destinationType}, creates TypeMap
* automatically if none exists.
*
* @param source type
* @param destination type
* @throws IllegalArgumentException is {@code sourceType}, {@code destinationType} are null
*/
public TypeMap typeMap(Class sourceType, Class destinationType) {
Assert.notNull(sourceType, "sourceType");
Assert.notNull(destinationType, "destinationType");
return config.typeMapStore.getOrCreate(null, sourceType, destinationType, null, engine);
}
/**
* Returns the TypeMap for the {@code sourceType}, {@code destinationType}, and {@code typeMapName}
* creates TypeMap automatically if none exists.
*
* @param source type
* @param destination type
* @throws IllegalArgumentException is {@code sourceType}, {@code destinationType} or
* {@code typeMapName} are null
*/
public TypeMap typeMap(Class sourceType, Class destinationType,
String typeMapName) {
Assert.notNull(sourceType, "sourceType");
Assert.notNull(destinationType, "destinationType");
Assert.notNull(typeMapName, "typeMapName");
return config.typeMapStore.getOrCreate(null, sourceType, destinationType, typeMapName, engine);
}
/**
* Creates an empty TypeMap for the {@code sourceType}, {@code destinationType}.
*
* @param source type
* @param destination type
* @throws IllegalArgumentException is {@code sourceType} or {@code destinationType} are null, or {@code TypeMap TypeMap emptyTypeMap(Class sourceType, Class destinationType) {
Assert.notNull(sourceType, "sourceType");
Assert.notNull(destinationType, "destinationType");
Assert.isNull(config.typeMapStore.get(sourceType, destinationType, null), "TypeMap already defined");
return config.typeMapStore.createEmptyTypeMap(null, sourceType, destinationType, null, config, engine);
}
/**
* Returns all TypeMaps for the ModelMapper.
*/
public Collection> getTypeMaps() {
return config.typeMapStore.get();
}
/**
* Maps {@code source} to an instance of {@code destinationType}. Mapping is performed according
* to the corresponding TypeMap. If no TypeMap exists for {@code source.getClass()} and
* {@code destinationType} then one is created.
*
* @param destination type
* @param source object to map from
* @param destinationType type to map to
* @return fully mapped instance of {@code destinationType}
* @throws IllegalArgumentException if {@code source} or {@code destinationType} are null
* @throws ConfigurationException if the ModelMapper cannot find or create a TypeMap for the
* arguments
* @throws MappingException if a runtime error occurs while mapping
*/
public D map(Object source, Class destinationType) {
Assert.notNull(source, "source");
Assert.notNull(destinationType, "destinationType");
return mapInternal(source, null, destinationType, null);
}
/**
* Maps {@code source} to an instance of {@code destinationType}. Mapping is performed according
* to the corresponding TypeMap for the {@code typeMapName}. If no TypeMap exists for the
* {@code source.getClass()}, {@code destinationType} and {@code typeMapName} then one is created.
*
* @param destination type
* @param source object to map from
* @param destinationType type to map to
* @param typeMapName name of existing TypeMap to use mappings from
* @return fully mapped instance of {@code destinationType}
* @throws IllegalArgumentException if {@code source}, {@code destinationType} or
* {@code typeMapName} are null
* @throws ConfigurationException if the ModelMapper cannot find or create a TypeMap for the
* arguments
* @throws MappingException if a runtime error occurs while mapping
*/
public D map(Object source, Class destinationType, String typeMapName) {
Assert.notNull(source, "source");
Assert.notNull(destinationType, "destinationType");
Assert.notNull(typeMapName, "typeMapName");
return mapInternal(source, null, destinationType, typeMapName);
}
/**
* Maps {@code source} to {@code destination}. Mapping is performed according to the corresponding
* TypeMap. If no TypeMap exists for {@code source.getClass()} and {@code destination.getClass()}
* then one is created.
*
* @param source object to map from
* @param destination object to map to
* @throws IllegalArgumentException if {@code source} or {@code destination} are null
* @throws ConfigurationException if the ModelMapper cannot find or create a TypeMap for the
* arguments
* @throws MappingException if an error occurs while mapping
*/
public void map(Object source, Object destination) {
Assert.notNull(source, "source");
Assert.notNull(destination, "destination");
mapInternal(source, destination, null, null);
}
/**
* Maps {@code source} to {@code destination}. Mapping is performed according to the corresponding
* TypeMap for the {@code typeMapName}. If no TypeMap exists for the {@code source.getClass()},
* {@code destination.getClass()} and {@code typeMapName} then one is created.
*
* @param source object to map from
* @param destination object to map to
* @param typeMapName name of existing TypeMap to use mappings from
* @throws IllegalArgumentException if {@code source}, {@code destination} or {@code typeMapName}
* are null
* @throws ConfigurationException if the ModelMapper cannot find or create a TypeMap for the
* arguments
* @throws MappingException if an error occurs while mapping
*/
public void map(Object source, Object destination, String typeMapName) {
Assert.notNull(source, "source");
Assert.notNull(destination, "destination");
Assert.notNull(typeMapName, "typeMapName");
mapInternal(source, destination, null, typeMapName);
}
/**
* Maps {@code source} to an instance of {@code destinationType}. Mapping is performed according
* to the corresponding TypeMap. If no TypeMap exists for {@code source.getClass()} and
* {@code destinationType} then one is created.
*
*
* To map a parameterized destination type, subclass {@link TypeToken} and obtain its Type:
*
*
* Type listType = new TypeToken<List<String>>() {}.getType();
* List<String> strings = modelMapper.map(source, listType);
*
*
* @param destination type
* @param source object to map from
* @param destinationType type to map to
* @return fully mapped instance of {@code destinationType}
* @throws IllegalArgumentException if {@code source} or {@code destinationType} are null
* @throws ConfigurationException if the ModelMapper cannot find or create the TypeMap
* @throws MappingException if a runtime error occurs while mapping
*/
public D map(Object source, Type destinationType) {
Assert.notNull(source, "source");
Assert.notNull(destinationType, "destinationType");
return mapInternal(source, null, destinationType, null);
}
/**
* Maps {@code source} to an instance of {@code destinationType}. Mapping is performed according
* to the corresponding TypeMap for the {@code typeMapName}. If no TypeMap exists for the
* {@code source.getClass()}, {@code destination.getClass()} and {@code typeMapName} then one is
* created.
*
*
* To map a parameterized destination type, subclass {@link TypeToken} and obtain its Type:
*
*
* Type listType = new TypeToken<List<String>>() {}.getType();
* List<String> strings = modelMapper.map(source, listType, "string-list");
*
*
* @param destination type
* @param source object to map from
* @param destinationType type to map to
* @param typeMapName name of existing TypeMap to use mappings from
* @return fully mapped instance of {@code destinationType}
* @throws IllegalArgumentException if {@code source}, {@code destinationType} or
* {@code typeMapName} are null
* @throws ConfigurationException if the ModelMapper cannot find or create the TypeMap
* @throws MappingException if a runtime error occurs while mapping
*/
public D map(Object source, Type destinationType, String typeMapName) {
Assert.notNull(source, "source");
Assert.notNull(destinationType, "destinationType");
Assert.notNull(typeMapName, "typeMapName");
return mapInternal(source, null, destinationType, typeMapName);
}
/**
* Validates that every top level destination property for each configured TypeMap is
* mapped to one and only one source property, or that a {@code Converter} was
* {@link TypeMap#setConverter(Converter) set} for the TypeMap. If not, a ConfigurationException
* is thrown detailing any missing mappings.
*
* @throws ValidationException if any TypeMaps contain unmapped properties
*/
public void validate() {
Errors errors = new Errors();
for (TypeMap, ?> typeMap : getTypeMaps()) {
try {
typeMap.validate();
} catch (ValidationException e) {
errors.merge(e.getErrorMessages());
}
}
errors.throwValidationExceptionIfErrorsExist();
}
/**
* Register a module
*
* @param module a module for extension
*/
public ModelMapper registerModule(Module module) {
module.setupModule(this);
return this;
}
private TypeMap createTypeMapInternal(S source, Class sourceType,
Class destinationType, String typeMapName, Configuration configuration) {
if (source != null)
sourceType = Types.deProxy(source.getClass());
Assert.state(config.typeMapStore.get(sourceType, destinationType, typeMapName) == null,
String.format("A TypeMap already exists for %s and %s", sourceType, destinationType));
return config.typeMapStore.create(source, sourceType, destinationType, typeMapName,
(InheritingConfiguration) configuration, engine);
}
private D mapInternal(Object source, D destination, Type destinationType, String typeMapName) {
if (destination != null)
destinationType = Types.deProxy(destination.getClass());
return engine.