diff --git a/core/src/main/java/org/mapstruct/BeanMapping.java b/core/src/main/java/org/mapstruct/BeanMapping.java index 6b062dac9..55ab05ae5 100644 --- a/core/src/main/java/org/mapstruct/BeanMapping.java +++ b/core/src/main/java/org/mapstruct/BeanMapping.java @@ -18,6 +18,34 @@ import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION; *
* Either {@link #resultType()}, {@link #qualifiedBy()} or {@link #nullValueMappingStrategy()} must be specified. *
+ *Example: Determining the result type
+ *
+ * // When result types have an inheritance relation, selecting either mapping method {@link Mapping} or factory method
+ * // {@link BeanMapping} can be become ambiguous. Parameter {@link BeanMapping#resultType()} can be used.
+ * public class FruitFactory {
+ * public Apple createApple() {
+ * return new Apple();
+ * }
+ * public Orange createOrange() {
+ * return new Orange();
+ * }
+ * }
+ * @Mapper(uses = FruitFactory.class)
+ * public interface FruitMapper {
+ * @BeanMapping(resultType = Apple.class)
+ * Fruit toFruit(FruitDto fruitDto);
+ * }
+ *
+ *
+ * // generates
+ * public class FruitMapperImpl implements FruitMapper {
+ * @Override
+ * public Fruit toFruit(FruitDto fruitDto) {
+ * Apple fruit = fruitFactory.createApple();
+ * // ...
+ * }
+ * }
+ *
*
* @author Sjaak Derksen
*/
@@ -40,9 +68,9 @@ public @interface BeanMapping {
* A qualifier is a custom annotation and can be placed on either a hand written mapper class or a method.
*
* @return the qualifiers
- * @see BeanMapping#qualifiedByName()
+ * @see Qualifier
*/
- Class extends Annotation>[] qualifiedBy() default { };
+ Class extends Annotation>[] qualifiedBy() default {};
/**
* Similar to {@link #qualifiedBy()}, but used in combination with {@code @}{@link Named} in case no custom
diff --git a/core/src/main/java/org/mapstruct/Builder.java b/core/src/main/java/org/mapstruct/Builder.java
index 2f3c9b2e9..449c4ac05 100644
--- a/core/src/main/java/org/mapstruct/Builder.java
+++ b/core/src/main/java/org/mapstruct/Builder.java
@@ -14,6 +14,32 @@ import org.mapstruct.util.Experimental;
/**
* Configuration of builders, e.g. the name of the final build method.
*
+ * + * Example: Using builder + *
+ *
+ * // Mapper
+ * @Mapper
+ * public interface SimpleBuilderMapper {
+ * @Mapping(target = "name", source = "fullName"),
+ * @Mapping(target = "job", constant = "programmer"),
+ * SimpleImmutablePerson toImmutable(SimpleMutablePerson source);
+ * }
+ *
+ *
+ * // generates
+ * @Override
+ * public SimpleImmutablePerson toImmutable(SimpleMutablePerson source) {
+ * // name method can be changed with parameter {@link #buildMethod()}
+ * Builder simpleImmutablePerson = SimpleImmutablePerson.builder();
+ * simpleImmutablePerson.name( source.getFullName() );
+ * simpleImmutablePerson.age( source.getAge() );
+ * simpleImmutablePerson.address( source.getAddress() );
+ * simpleImmutablePerson.job( "programmer" );
+ * // ...
+ * }
+ *
+ *
* @author Filip Hrisafov
*
* @since 1.3
diff --git a/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java b/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java
index d1bede3f0..b1eec47f3 100644
--- a/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java
+++ b/core/src/main/java/org/mapstruct/InheritInverseConfiguration.java
@@ -22,6 +22,41 @@ import java.lang.annotation.Target;
* If more than one matching inverse method exists, the name of the method to inherit the configuration from must be
* specified via {@link #name()}
*
+ * + * Example + *
+ *
+ * @Mapper
+ * public interface HumanMapper {
+ * Human toHuman(HumanDto humanDto);
+ * @InheritInverseConfiguration
+ * HumanDto toHumanDto(Human human);
+ * }
+ *
+ *
+ * // generates
+ * public class HumanMapperImpl implements HumanMapper {
+ * @Override
+ * public Human toHuman(HumanDto humanDto) {
+ * if ( humanDto == null ) {
+ * return null;
+ * }
+ * Human human = new Human();
+ * human.setName( humanDto.getName() );
+ * return human;
+ * }
+ * @Override
+ * public HumanDto toHumanDto(Human human) {
+ * if ( human == null ) {
+ * return null;
+ * }
+ * HumanDto humanDto = new HumanDto();
+ * humanDto.setName( human.getName() );
+ * return humanDto;
+ * }
+ * }
+ *
+ *
* @author Sjaak Derksen
*/
@Target(ElementType.METHOD)
diff --git a/core/src/main/java/org/mapstruct/IterableMapping.java b/core/src/main/java/org/mapstruct/IterableMapping.java
index 0e81e6612..3041c4bfd 100644
--- a/core/src/main/java/org/mapstruct/IterableMapping.java
+++ b/core/src/main/java/org/mapstruct/IterableMapping.java
@@ -18,9 +18,33 @@ import java.util.Date;
* Configures the mapping between two iterable like types, e.g. {@code ListNote: either @IterableMapping#dateFormat, @IterableMapping#resultType or @IterableMapping#qualifiedBy + *
Note: either {@link #dateFormat()}, {@link #elementTargetType()} or {@link #qualifiedBy() } * must be specified
* + *+ * Example: Convert List<Float> to List<String> + *
+ *
+ * @Mapper
+ * public interface FloatToStringMapper {
+ * @IterableMapping( numberFormat = "##.00" )
+ * List<String> sourceToTarget(List<Float> source);
+ * }
+ *
+ *
+ * // generates
+ * public class FloatToStringMapperImpl implements FloatToStringMapper {
+ * @Override
+ * public List<String> sourceToTarget(List<Float> source) {
+ * List<String> list = new ArrayList<String>( source.size() );
+ * for ( Float float1 : source ) {
+ * list.add( new DecimalFormat( "##.00" ).format( float1 ) );
+ * }
+ * // ...
+ * }
+ * }
+ *
+ *
* Supported mappings are:
* Note: at least one element needs to be specified
+ *+ * Example: + *
+ *
+ * @Mapper
+ * public interface SimpleMapper {
+ * @MapMapping(valueDateFormat = "dd.MM.yyyy")
+ * Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source);
+ * }
+ *
+ *
+ * // generates
+ * public class SimpleMapperImpl implements SimpleMapper {
+ * @Override
+ * public Map<String, String> longDateMapToStringStringMap(Map<Long, Date> source) } {
+ * Map<String, String> map = new HashMap<String, String>(); }
+ * for ( java.util.Map.Entry<Long, Date> entry : source.entrySet() ) } {
+ * String key = new DecimalFormat( "" ).format( entry.getKey() );
+ * String value = new SimpleDateFormat( "dd.MM.yyyy" ).format( entry.getValue() );
+ * map.put( key, value );
+ * }
+ * // ...
+ * }
+ * }
+ *
+ *
+ * NOTE: at least one element needs to be specified
* * @author Gunnar Morling */ @@ -66,6 +92,7 @@ public @interface MapMapping { * A qualifier is a custom annotation and can be placed on either a hand written mapper class or a method. * * @return the qualifiers + * @see Qualifier */ Class extends Annotation>[] keyQualifiedBy() default { }; @@ -93,6 +120,7 @@ public @interface MapMapping { * A qualifier is a custom annotation and can be placed on either a hand written mapper class or a method. * * @return the qualifiers + * @see Qualifier */ Class extends Annotation>[] valueQualifiedBy() default { }; diff --git a/core/src/main/java/org/mapstruct/Mapper.java b/core/src/main/java/org/mapstruct/Mapper.java index 69e70e859..52d12426f 100644 --- a/core/src/main/java/org/mapstruct/Mapper.java +++ b/core/src/main/java/org/mapstruct/Mapper.java @@ -18,6 +18,58 @@ import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION; * Marks an interface or abstract class as a mapper and activates the generation of a implementation of that type via * MapStruct. * + *+ * Example 1: Creating mapper + *
+ *
+ * @Mapper
+ * public interface CarMapper {
+ * CarDto toCarDto(Car source);
+ * }
+ *
+ * + * Example 2: Use additional mappers with parameters {@link #uses()}, {@link #componentModel()} + * and {@link #injectionStrategy()} + *
+ *
+ * // we have MarkMapper (map field "mark" to field "name" to upper case)
+ * @Mapper(componentModel = "spring")
+ * public class MarkMapper {
+ * public String mapMark(String mark) {
+ * return mark.toUpperCase();
+ * }
+ * }
+ * // we have CarMapper
+ * @Mapper(
+ * componentModel = "spring",
+ * uses = MarkMapper.class,
+ * injectionStrategy = InjectionStrategy.CONSTRUCTOR)
+ * public interface CarMapper {
+ * @Mapping(source = "mark", target = "name")
+ * CarDto convertMap(CarEntity carEntity);
+ * }
+ *
+ *
+ * // generates
+ * @Component
+ * public class CarMapperImpl implements CarMapper {
+ * private final MarkMapper markMapper;
+ * @Autowired
+ * public CarMapperImpl(MarkMapper markMapper) {
+ * this.markMapper = markMapper;
+ * }
+ * @Override
+ * public CarDto convertMap(CarEntity carEntity) {
+ * if ( carEntity == null ) {
+ * return null;
+ * }
+ * CarDto carDto = new CarDto();
+ * carDto.setName( markMapper.mapMark( carEntity.getMark() ) );
+ * return carDto;
+ * }
+ * }
+ *
+ *
* @author Gunnar Morling
*/
@Target(ElementType.TYPE)
diff --git a/core/src/main/java/org/mapstruct/MapperConfig.java b/core/src/main/java/org/mapstruct/MapperConfig.java
index 079388cc4..c03217f08 100644
--- a/core/src/main/java/org/mapstruct/MapperConfig.java
+++ b/core/src/main/java/org/mapstruct/MapperConfig.java
@@ -30,6 +30,36 @@ import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
* types are assignable.
*
*
+ * + * Example: + *
+ *
+ * // create config
+ * @MapperConfig(
+ * uses = CustomMapperViaMapperConfig.class,
+ * unmappedTargetPolicy = ReportingPolicy.ERROR
+ * )
+ * public interface CentralConfig {
+ * }
+ *
+ *
+ * // use config
+ * @Mapper(config = CentralConfig.class, uses = { CustomMapperViaMapper.class } )
+ * public interface SourceTargetMapper {
+ * // ...
+ * }
+ *
+ *
+ * // result after applying CentralConfig
+ * @Mapper(
+ * uses = { CustomMapperViaMapper.class, CustomMapperViaMapperConfig.class },
+ * unmappedTargetPolicy = ReportingPolicy.ERROR
+ * )
+ * public interface SourceTargetMapper {
+ * // ...
+ * }
+ *
+ *
* @author Sjaak Derksen
* @see Mapper#config()
*/
diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java
index a12aeba70..92bf5742e 100644
--- a/core/src/main/java/org/mapstruct/Mapping.java
+++ b/core/src/main/java/org/mapstruct/Mapping.java
@@ -16,17 +16,124 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION;
+
/**
* Configures the mapping of one bean attribute or enum constant.
* * The name of the mapped attribute or constant is to be specified via {@link #target()}. For mapped bean attributes it * is assumed by default that the attribute has the same name in the source bean. Alternatively, one of * {@link #source()}, {@link #expression()} or {@link #constant()} can be specified to define the property source. + *
** In addition, the attributes {@link #dateFormat()} and {@link #qualifiedBy()} may be used to further define the * mapping. + *
* *+ * Example 1: Implicitly mapping fields with the same name: + *
+ *
+ * // Both classes HumanDto and Human have property with name "fullName"
+ * // properties with the same name will be mapped implicitly
+ * @Mapper
+ * public interface HumanMapper {
+ * HumanDto toHumanDto(Human human)
+ * }
+ *
+ *
+ * // generates:
+ * @Override
+ * public HumanDto toHumanDto(Human human) {
+ * humanDto.setFullName( human.getFullName() );
+ * // ...
+ * }
+ *
+ *
+ * Example 2: Mapping properties with different names
+ *
+ * // We need map Human.companyName to HumanDto.company
+ * // we can use @Mapping with parameters {@link #source()} and {@link #source()}
+ * @Mapper
+ * public interface HumanMapper {
+ * @Mapping(source="companyName", target="company")
+ * HumanDto toHumanDto(Human human)
+ * }
+ *
+ *
+ * // generates:
+ * @Override
+ * public HumanDto toHumanDto(Human human) {
+ * humanDto.setCompany( human.getCompanyName() );
+ * // ...
+ * }
+ *
+ * + * Example 3: Mapping with expression + * IMPORTANT NOTE: Now it works only for Java + *
+ *
+ * // We need map Human.name to HumanDto.countNameSymbols.
+ * // we can use {@link #expression()} for it
+ * @Mapper
+ * public interface HumanMapper {
+ * @Mapping(target="countNameSymbols", expression="java(human.getName().length())")
+ * HumanDto toHumanDto(Human human)
+ * }
+ *
+ *
+ * // generates:
+ *@Override
+ * public HumanDto toHumanDto(Human human) {
+ * humanDto.setCountNameSymbols( human.getName().length() );
+ * //...
+ * }
+ *
+ * + * Example 4: Mapping to constant + *
+ *
+ * // We need map HumanDto.name to string constant "Unknown"
+ * // we can use {@link #constant()} for it
+ * @Mapper
+ * public interface HumanMapper {
+ * @Mapping(target="name", constant="Unknown")
+ * HumanDto toHumanDto(Human human)
+ * }
+ *
+ *
+ * // generates
+ * @Override
+ * public HumanDto toHumanDto(Human human) {
+ * humanDto.setName( "Unknown" );
+ * // ...
+ * }
+ *
+ * + * Example 5: Mapping with default value + *
+ *
+ * // We need map Human.name to HumanDto.fullName, but if Human.name == null, then set value "Somebody"
+ * // we can use {@link #defaultValue()} or {@link #defaultExpression()} for it
+ * @Mapper
+ * public interface HumanMapper {
+ * @Mapping(source="name", target="name", defaultValue="Somebody")
+ * HumanDto toHumanDto(Human human)
+ * }
+ *
+ *
+ * // generates
+ * @Override
+ * public HumanDto toHumanDto(Human human) {
+ * if ( human.getName() != null ) {
+ * humanDto.setFullName( human.getName() );
+ * }
+ * else {
+ * humanDto.setFullName( "Somebody" );
+ * }
+ * // ...
+ * }
+ *
+ *
* IMPORTANT NOTE: the enum mapping capability is deprecated and replaced by {@link ValueMapping} it
* will be removed in subsequent versions.
*
@@ -188,6 +295,7 @@ public @interface Mapping {
* error. A qualifier is a custom annotation and can be placed on a hand written mapper class or a method.
*
* @return the qualifiers
+ * @see Qualifier
*/
Class extends Annotation>[] qualifiedBy() default { };
diff --git a/core/src/main/java/org/mapstruct/MappingTarget.java b/core/src/main/java/org/mapstruct/MappingTarget.java
index fc140f120..391644836 100644
--- a/core/src/main/java/org/mapstruct/MappingTarget.java
+++ b/core/src/main/java/org/mapstruct/MappingTarget.java
@@ -17,6 +17,43 @@ import java.lang.annotation.Target;
* * NOTE: The parameter passed as a mapping target must not be {@code null}. * + *
+ * Example 1: Update exist bean without return value + *
+ *
+ * @Mapper
+ * public interface HumanMapper {
+ * void updateHuman(HumanDto humanDto, @MappingTarget Human human);
+ * }
+ *
+ *
+ * // generates
+ * @Override
+ * public void updateHuman(HumanDto humanDto, Human human) {
+ * human.setName( humanDto.getName() );
+ * // ...
+ * }
+ *
+ * + * Example 2: Update exist bean and return it + *
+ *
+ * @Mapper
+ * public interface HumanMapper {
+ * Human updateHuman(HumanDto humanDto, @MappingTarget Human human);
+ * }
+ *
+ * // generates:
+ *
+ * @Override
+ * public Human updateHuman(HumanDto humanDto, Human human) {
+ * // ...
+ * human.setName( humanDto.getName() );
+ * return human;
+ * }
+ *
+ *
+ *
* @author Andreas Gudian
*/
@Target(ElementType.PARAMETER)
diff --git a/core/src/main/java/org/mapstruct/Mappings.java b/core/src/main/java/org/mapstruct/Mappings.java
index 1e9dd9670..37e33771d 100644
--- a/core/src/main/java/org/mapstruct/Mappings.java
+++ b/core/src/main/java/org/mapstruct/Mappings.java
@@ -12,6 +12,32 @@ import java.lang.annotation.Target;
/**
* Configures the mappings of several bean attributes.
+ * + * TIP: When using Java 8 or later, you can omit the @Mappings + * wrapper annotation and directly specify several @Mapping annotations on one method. + * + *
These two examples are equal. + *
+ *
+ * // before Java 8
+ * @Mapper
+ * public interface MyMapper {
+ * @Mappings({
+ * @Mapping(source = "first", target = "firstProperty"),
+ * @Mapping(source = "second", target = "secondProperty")
+ * })
+ * HumanDto toHumanDto(Human human);
+ * }
+ *
+ *
+ * // Java 8 and later
+ * @Mapper
+ * public interface MyMapper {
+ * @Mapping(source = "first", target = "firstProperty"),
+ * @Mapping(source = "second", target = "secondProperty")
+ * HumanDto toHumanDto(Human human);
+ * }
+ *
*
* @author Gunnar Morling
*/
diff --git a/core/src/main/java/org/mapstruct/Qualifier.java b/core/src/main/java/org/mapstruct/Qualifier.java
index c21390e8c..be51f49e0 100644
--- a/core/src/main/java/org/mapstruct/Qualifier.java
+++ b/core/src/main/java/org/mapstruct/Qualifier.java
@@ -14,7 +14,7 @@ import java.lang.annotation.Target;
* Declares an annotation type to be a qualifier. Qualifier annotations allow unambiguously identify a suitable mapping
* method in case several methods qualify to map a bean property, iterable element etc.
* - * For more info see: + * Can be used in: *
Example:
+ *
+ * // create qualifiers
+ * @Qualifier
+ * @Target(ElementType.TYPE)
+ * @Retention(RetentionPolicy.CLASS)
+ * public @interface TitleTranslator {}
*
- *
* @Qualifier
* @Target(ElementType.METHOD)
* @Retention(RetentionPolicy.CLASS)
- * public @interface EnglishToGerman {
+ * public @interface EnglishToGerman {}
+ *
+ * @Qualifier
+ * @Target(ElementType.METHOD)
+ * @Retention(RetentionPolicy.CLASS)
+ * public @interface GermanToEnglish {}
+ *
+ *
+ * // we can create class with map methods
+ * @TitleTranslator
+ * public class Titles {
+ * @EnglishToGerman
+ * public String translateTitleEnglishToGerman(String title) {
+ * // some mapping logic
+ * }
+ * @GermanToEnglish
+ * public String translateTitleGermanToEnglish(String title) {
+ * // some mapping logic
+ * }
* }
- *
+ *
+ *
+ * // usage
+ * @Mapper( uses = Titles.class )
+ * public interface MovieMapper {
+ * @Mapping( target = "title", qualifiedBy = { TitleTranslator.class, EnglishToGerman.class } )
+ * GermanRelease toGerman( OriginalRelease movies );
+ * }
+ *
+ *
+ * // generates
+ * public class MovieMapperImpl implements MovieMapper {
+ * private final Titles titles = new Titles();
+ * @Override
+ * public GermanRelease toGerman(OriginalRelease movies) {
+ * if ( movies == null ) {
+ * return null;
+ * }
+ * GermanRelease germanRelease = new GermanRelease();
+ * germanRelease.setTitle( titles.translateTitleEnglishToGerman( movies.getTitle() ) );
+ * return germanRelease;
+ * }
+ * }
+ *
*
* NOTE: Qualifiers should have {@link RetentionPolicy#CLASS}.
*
diff --git a/core/src/main/java/org/mapstruct/TargetType.java b/core/src/main/java/org/mapstruct/TargetType.java
index e4064f248..9d617fec0 100644
--- a/core/src/main/java/org/mapstruct/TargetType.java
+++ b/core/src/main/java/org/mapstruct/TargetType.java
@@ -16,6 +16,35 @@ import java.lang.annotation.Target;
* Not more than one parameter can be declared as {@code TargetType} and that parameter needs to be of type
* {@link Class} (may be parameterized), or a super-type of it.
*
+ * + * Example: + *
+ *
+ * public class EntityFactory {
+ * public <T extends BaseEntity> T createEntity(@TargetType Class<T> entityClass) {
+ * return // ... custom factory logic
+ * }
+ * }
+ * @Mapper(uses = EntityFactory.class)
+ * public interface CarMapper {
+ * CarEntity carDtoToCar(CarDto dto);
+ * }
+ *
+ *
+ * // generates
+ * public class CarMapperImpl implements CarMapper {
+ * private final EntityFactory entityFactory = new EntityFactory();
+ * @Override
+ * public CarEntity carDtoToCar(CarDto dto) {
+ * if ( dto == null ) {
+ * return null;
+ * }
+ * CarEntity carEntity = entityFactory.createEntity( CarEntity.class );
+ * return carEntity;
+ * }
+ * }
+ *
+ *
* @author Andreas Gudian
*/
@Target(ElementType.PARAMETER)
diff --git a/core/src/main/java/org/mapstruct/ValueMappings.java b/core/src/main/java/org/mapstruct/ValueMappings.java
index f2450a2fd..a1b466617 100644
--- a/core/src/main/java/org/mapstruct/ValueMappings.java
+++ b/core/src/main/java/org/mapstruct/ValueMappings.java
@@ -12,6 +12,31 @@ import java.lang.annotation.Target;
/**
* Constructs a set of value (constant) mappings.
+ * + * TIP: When using Java 8 or later, you can omit the @ValueMappings + * wrapper annotation and directly specify several @ValueMapping annotations on one method. + * + *
These two examples are equal
+ *
+ * // before Java 8
+ * @Mapper
+ * public interface GenderMapper {
+ * @ValueMappings({
+ * @ValueMapping(source = "MALE", target = "M"),
+ * @ValueMapping(source = "FEMALE", target = "F")
+ * })
+ * GenderDto mapToDto(Gender gender);
+ * }
+ *
+ *
+ * //Java 8 and later
+ * @Mapper
+ * public interface GenderMapper {
+ * @ValueMapping(source = "MALE", target = "M"),
+ * @ValueMapping(source = "FEMALE", target = "F")
+ * GenderDto mapToDto(Gender gender);
+ * }
+ *
*
* @author Sjaak Derksen
*/