All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.modelmapper.ModelMapper Maven / Gradle / Ivy

There is a newer version: 3.2.1
Show newest version
/*
 * 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.map(source, Types.deProxy(source.getClass()), destination, TypeToken.of(destinationType), typeMapName); } }