mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#73 Add support for using constructor arguments when instantiating mapping targets
By default the constructor argument names are used to extract the target properties. If a constructor is annotated with an annotation named `@ConstructorProperties` (from any package) then it would be used to extract the target properties. If a mapping target has a parameterless empty constructor it would be used to instantiate the target. When there are multiple constructors then an annotation named `@Default` (from any package) can be used to mark a constructor that should be used by default when instantiating the target. Supports mapping into Java 14 Records and Kotlin data classes out of the box
This commit is contained in:
parent
d6ff5204d7
commit
2b2299a730
@ -122,9 +122,6 @@
|
||||
|
||||
<!-- Checks for Size Violations. -->
|
||||
<!-- See http://checkstyle.sf.net/config_sizes.html -->
|
||||
<module name="MethodLength">
|
||||
<property name="max" value="200"/>
|
||||
</module>
|
||||
<module name="ParameterNumber">
|
||||
<property name="max" value="10"/>
|
||||
</module>
|
||||
|
@ -528,3 +528,80 @@ Otherwise, you would need to write a custom `BuilderProvider`
|
||||
====
|
||||
In case you want to disable using builders then you can use the `NoOpBuilderProvider` by creating a `org.mapstruct.ap.spi.BuilderProvider` file in the `META-INF/services` directory with `org.mapstruct.ap.spi.NoOpBuilderProvider` as it's content.
|
||||
====
|
||||
|
||||
[[mapping-with-constructors]]
|
||||
=== Using Constructors
|
||||
|
||||
MapStruct supports using constructors for mapping target types.
|
||||
When doing a mapping MapStruct checks if there is a builder for the type being mapped.
|
||||
If there is no builder, then MapStruct looks for a single accessible constructor.
|
||||
When there are multiple constructors then the following is done to pick the one which should be used:
|
||||
|
||||
* If a parameterless constructor exists then it would be used to construct the object, and the other constructors will be ignored
|
||||
* If there are multiple constructors then the one annotated with annotation named `@Default` (from any package) will be used
|
||||
|
||||
When using a constructor then the names of the parameters of the constructor will be used and matched to the target properties.
|
||||
When the constructor has an annotation named `@ConstructorProperties` (from any package) then this annotation will be used to get the names of the parameters.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
When an object factory method or a method annotated with `@ObjectFactory` exists, it will take precedence over any constructor defined in the target.
|
||||
The target object constructor will not be used in that case.
|
||||
====
|
||||
|
||||
|
||||
.Person with constructor parameters
|
||||
====
|
||||
[source, java, linenums]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
public class Person {
|
||||
|
||||
private final String name;
|
||||
private final String surname;
|
||||
|
||||
public Person(String name, String surname) {
|
||||
this.name = name;
|
||||
this.surname = surname;
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
.Person With Constructor Mapper definition
|
||||
====
|
||||
[source, java, linenums]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
public interface PersonMapper {
|
||||
|
||||
Person map(PersonDto dto);
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
.Generated mapper with constructor
|
||||
====
|
||||
[source, java, linenums]
|
||||
[subs="verbatim,attributes"]
|
||||
----
|
||||
// GENERATED CODE
|
||||
public class PersonMapperImpl implements PersonMapper {
|
||||
|
||||
public Person map(PersonDto dto) {
|
||||
if (dto == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String name;
|
||||
String surname;
|
||||
name = dto.getName();
|
||||
surname = dto.getSurname();
|
||||
|
||||
Person person = new Person( name, surname );
|
||||
|
||||
return person;
|
||||
}
|
||||
}
|
||||
----
|
||||
====
|
||||
|
@ -174,6 +174,12 @@ During the generation of automatic sub-mapping methods <<shared-configurations>>
|
||||
Follow issue https://github.com/mapstruct/mapstruct/issues/1086[#1086] for more information.
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Constructor properties of the target object are also considered as target properties.
|
||||
You can read more about that in <<mapping-with-constructors>>
|
||||
====
|
||||
|
||||
[[controlling-nested-bean-mappings]]
|
||||
=== Controlling nested bean mappings
|
||||
|
||||
|
@ -107,6 +107,14 @@ public class MavenIntegrationTest {
|
||||
void recordsTest() {
|
||||
}
|
||||
|
||||
@ProcessorTest(baseDir = "kotlinDataTest", processorTypes = {
|
||||
ProcessorTest.ProcessorType.JAVAC
|
||||
}, forkJvm = true)
|
||||
// We have to fork the jvm because there is an NPE in com.intellij.openapi.util.SystemInfo.getRtVersion
|
||||
// and the kotlin-maven-plugin uses that. See also https://youtrack.jetbrains.com/issue/IDEA-238907
|
||||
void kotlinDataTest() {
|
||||
}
|
||||
|
||||
@ProcessorTest(baseDir = "simpleTest")
|
||||
void simpleTest() {
|
||||
}
|
||||
|
@ -71,6 +71,9 @@ public class ProcessorInvocationInterceptor implements InvocationInterceptor {
|
||||
}
|
||||
else {
|
||||
verifier = new Verifier( destination.getCanonicalPath() );
|
||||
if ( processorTestContext.isForkJvm() ) {
|
||||
verifier.setForkJvm( true );
|
||||
}
|
||||
}
|
||||
|
||||
List<String> goals = new ArrayList<>( 3 );
|
||||
|
@ -106,4 +106,7 @@ public @interface ProcessorTest {
|
||||
* @return the {@link CommandLineEnhancer} implementation. Must have a default constructor.
|
||||
*/
|
||||
Class<? extends CommandLineEnhancer> commandLineEnhancer() default CommandLineEnhancer.class;
|
||||
|
||||
boolean forkJvm() default false;
|
||||
|
||||
}
|
||||
|
@ -13,13 +13,16 @@ public class ProcessorTestContext {
|
||||
private final String baseDir;
|
||||
private final ProcessorTest.ProcessorType processor;
|
||||
private final Class<? extends ProcessorTest.CommandLineEnhancer> cliEnhancerClass;
|
||||
private final boolean forkJvm;
|
||||
|
||||
public ProcessorTestContext(String baseDir,
|
||||
ProcessorTest.ProcessorType processor,
|
||||
Class<? extends ProcessorTest.CommandLineEnhancer> cliEnhancerClass) {
|
||||
Class<? extends ProcessorTest.CommandLineEnhancer> cliEnhancerClass,
|
||||
boolean forkJvm) {
|
||||
this.baseDir = baseDir;
|
||||
this.processor = processor;
|
||||
this.cliEnhancerClass = cliEnhancerClass;
|
||||
this.forkJvm = forkJvm;
|
||||
}
|
||||
|
||||
public String getBaseDir() {
|
||||
@ -33,4 +36,8 @@ public class ProcessorTestContext {
|
||||
public Class<? extends ProcessorTest.CommandLineEnhancer> getCliEnhancerClass() {
|
||||
return cliEnhancerClass;
|
||||
}
|
||||
|
||||
public boolean isForkJvm() {
|
||||
return forkJvm;
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,8 @@ public class ProcessorTestTemplateInvocationContextProvider implements TestTempl
|
||||
.map( processorType -> new ProcessorTestTemplateInvocationContext( new ProcessorTestContext(
|
||||
processorTest.baseDir(),
|
||||
processorType,
|
||||
processorTest.commandLineEnhancer()
|
||||
processorTest.commandLineEnhancer(),
|
||||
processorTest.forkJvm()
|
||||
) ) );
|
||||
}
|
||||
}
|
||||
|
102
integrationtest/src/test/resources/kotlinDataTest/pom.xml
Normal file
102
integrationtest/src/test/resources/kotlinDataTest/pom.xml
Normal file
@ -0,0 +1,102 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
|
||||
Copyright MapStruct Authors.
|
||||
|
||||
Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
-->
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>org.mapstruct</groupId>
|
||||
<artifactId>mapstruct-it-parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
<artifactId>kotlinDataTest</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<properties>
|
||||
<kotlin.version>1.3.70</kotlin.version>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>generate-via-compiler-plugin</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<goals> <goal>compile</goal> </goals>
|
||||
<configuration>
|
||||
<sourceDirs>
|
||||
<sourceDir>\${project.basedir}/src/main/kotlin</sourceDir>
|
||||
<sourceDir>\${project.basedir}/src/main/java</sourceDir>
|
||||
</sourceDirs>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<goals> <goal>test-compile</goal> </goals>
|
||||
<configuration>
|
||||
<sourceDirs>
|
||||
<sourceDir>\${project.basedir}/src/test/kotlin</sourceDir>
|
||||
<sourceDir>\${project.basedir}/src/test/java</sourceDir>
|
||||
</sourceDirs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<executions>
|
||||
<!-- Replacing default-compile as it is treated specially by maven -->
|
||||
<execution>
|
||||
<id>default-compile</id>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
<!-- Replacing default-testCompile as it is treated specially by maven -->
|
||||
<execution>
|
||||
<id>default-testCompile</id>
|
||||
<phase>none</phase>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>java-compile</id>
|
||||
<phase>compile</phase>
|
||||
<goals> <goal>compile</goal> </goals>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>java-test-compile</id>
|
||||
<phase>test-compile</phase>
|
||||
<goals> <goal>testCompile</goal> </goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.itest.kotlin.data;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class CustomerEntity {
|
||||
|
||||
private String name;
|
||||
private String mail;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getMail() {
|
||||
return mail;
|
||||
}
|
||||
|
||||
public void setMail(String mail) {
|
||||
this.mail = mail;
|
||||
}
|
||||
}
|
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.itest.kotlin.data;
|
||||
|
||||
import org.mapstruct.InheritInverseConfiguration;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper
|
||||
public interface CustomerMapper {
|
||||
|
||||
CustomerMapper INSTANCE = Mappers.getMapper( CustomerMapper.class );
|
||||
|
||||
@Mapping(target = "mail", source = "email")
|
||||
CustomerEntity fromRecord(CustomerDto record);
|
||||
|
||||
@InheritInverseConfiguration
|
||||
CustomerDto toRecord(CustomerEntity entity);
|
||||
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.itest.kotlin.data;
|
||||
|
||||
data class CustomerDto(var name: String?, var email: String?) {
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.itest.kotlin.data;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.mapstruct.itest.kotlin.data.CustomerDto;
|
||||
import org.mapstruct.itest.kotlin.data.CustomerEntity;
|
||||
import org.mapstruct.itest.kotlin.data.CustomerMapper;
|
||||
|
||||
public class KotlinDataTest {
|
||||
|
||||
@Test
|
||||
public void shouldMapData() {
|
||||
CustomerEntity customer = CustomerMapper.INSTANCE.fromRecord( new CustomerDto( "Kermit", "kermit@test.com" ) );
|
||||
|
||||
assertThat( customer ).isNotNull();
|
||||
assertThat( customer.getName() ).isEqualTo( "Kermit" );
|
||||
assertThat( customer.getMail() ).isEqualTo( "kermit@test.com" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapIntoData() {
|
||||
CustomerEntity entity = new CustomerEntity();
|
||||
entity.setName( "Kermit" );
|
||||
entity.setMail( "kermit@test.com" );
|
||||
|
||||
CustomerDto customer = CustomerMapper.INSTANCE.toRecord( entity );
|
||||
|
||||
assertThat( customer ).isNotNull();
|
||||
assertThat( customer.getName() ).isEqualTo( "Kermit" );
|
||||
assertThat( customer.getEmail() ).isEqualTo( "kermit@test.com" );
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@
|
||||
*/
|
||||
package org.mapstruct.itest.records;
|
||||
|
||||
import org.mapstruct.InheritInverseConfiguration;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
@ -20,4 +21,7 @@ public interface CustomerMapper {
|
||||
@Mapping(target = "mail", source = "email")
|
||||
CustomerEntity fromRecord(CustomerDto record);
|
||||
|
||||
@InheritInverseConfiguration
|
||||
CustomerDto toRecord(CustomerEntity entity);
|
||||
|
||||
}
|
||||
|
@ -22,4 +22,17 @@ public class RecordsTest {
|
||||
assertThat( customer.getName() ).isEqualTo( "Kermit" );
|
||||
assertThat( customer.getMail() ).isEqualTo( "kermit@test.com" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapIntoRecord() {
|
||||
CustomerEntity entity = new CustomerEntity();
|
||||
entity.setName( "Kermit" );
|
||||
entity.setMail( "kermit@test.com" );
|
||||
|
||||
CustomerDto customer = CustomerMapper.INSTANCE.toRecord( entity );
|
||||
|
||||
assertThat( customer ).isNotNull();
|
||||
assertThat( customer.name() ).isEqualTo( "Kermit" );
|
||||
assertThat( customer.email() ).isEqualTo( "kermit@test.com" );
|
||||
}
|
||||
}
|
||||
|
@ -8,18 +8,26 @@ package org.mapstruct.ap.internal.model;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.util.ElementFilter;
|
||||
import javax.tools.Diagnostic;
|
||||
|
||||
import org.mapstruct.ap.internal.gem.CollectionMappingStrategyGem;
|
||||
@ -33,6 +41,7 @@ import org.mapstruct.ap.internal.model.beanmapping.SourceReference;
|
||||
import org.mapstruct.ap.internal.model.beanmapping.TargetReference;
|
||||
import org.mapstruct.ap.internal.model.common.BuilderType;
|
||||
import org.mapstruct.ap.internal.model.common.Parameter;
|
||||
import org.mapstruct.ap.internal.model.common.ParameterBinding;
|
||||
import org.mapstruct.ap.internal.model.common.Type;
|
||||
import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer;
|
||||
import org.mapstruct.ap.internal.model.dependency.GraphAnalyzer.GraphAnalyzerBuilder;
|
||||
@ -41,15 +50,20 @@ import org.mapstruct.ap.internal.model.source.MappingOptions;
|
||||
import org.mapstruct.ap.internal.model.source.Method;
|
||||
import org.mapstruct.ap.internal.model.source.SelectionParameters;
|
||||
import org.mapstruct.ap.internal.model.source.SourceMethod;
|
||||
import org.mapstruct.ap.internal.model.source.selector.SelectedMethod;
|
||||
import org.mapstruct.ap.internal.util.Message;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||
import org.mapstruct.ap.internal.util.accessor.AccessorType;
|
||||
import org.mapstruct.ap.internal.util.accessor.ParameterElementAccessor;
|
||||
|
||||
import static org.mapstruct.ap.internal.model.beanmapping.MappingReferences.forSourceMethod;
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
import static org.mapstruct.ap.internal.util.Message.BEANMAPPING_ABSTRACT;
|
||||
import static org.mapstruct.ap.internal.util.Message.BEANMAPPING_NOT_ASSIGNABLE;
|
||||
import static org.mapstruct.ap.internal.util.Message.GENERAL_ABSTRACT_RETURN_TYPE;
|
||||
import static org.mapstruct.ap.internal.util.Message.GENERAL_AMBIGIOUS_CONSTRUCTORS;
|
||||
import static org.mapstruct.ap.internal.util.Message.GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS;
|
||||
|
||||
/**
|
||||
* A {@link MappingMethod} implemented by a {@link Mapper} class which maps one bean type to another, optionally
|
||||
@ -61,7 +75,9 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
|
||||
private final List<PropertyMapping> propertyMappings;
|
||||
private final Map<String, List<PropertyMapping>> mappingsByParameter;
|
||||
private final Map<String, List<PropertyMapping>> constructorMappingsByParameter;
|
||||
private final List<PropertyMapping> constantMappings;
|
||||
private final List<PropertyMapping> constructorConstantMappings;
|
||||
private final Type returnTypeToConstruct;
|
||||
private final BuilderType returnTypeBuilder;
|
||||
private final MethodReference finalizerMethod;
|
||||
@ -73,6 +89,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
|
||||
/* returnType to construct can have a builder */
|
||||
private BuilderType returnTypeBuilder;
|
||||
private Map<String, Accessor> unprocessedConstructorProperties;
|
||||
private Map<String, Accessor> unprocessedTargetProperties;
|
||||
private Map<String, Accessor> unprocessedSourceProperties;
|
||||
private Set<String> targetProperties;
|
||||
@ -82,6 +99,8 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
private final Map<String, Set<MappingReference>> unprocessedDefinedTargets = new LinkedHashMap<>();
|
||||
|
||||
private MappingReferences mappingReferences;
|
||||
private MethodReference factoryMethod;
|
||||
private boolean hasFactoryMethod;
|
||||
|
||||
public Builder mappingContext(MappingBuilderContext mappingContext) {
|
||||
this.ctx = mappingContext;
|
||||
@ -125,15 +144,12 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
/* 3) or the builder whenever the return type is immutable */
|
||||
Type returnTypeToConstruct = null;
|
||||
|
||||
/* factory or builder method to construct the returnTypeToConstruct */
|
||||
MethodReference factoryMethod = null;
|
||||
|
||||
// determine which return type to construct
|
||||
boolean cannotConstructReturnType = false;
|
||||
if ( !method.getReturnType().isVoid() ) {
|
||||
Type returnTypeImpl = getReturnTypeToConstructFromSelectionParameters( selectionParameters );
|
||||
if ( returnTypeImpl != null ) {
|
||||
factoryMethod = getFactoryMethod( returnTypeImpl, selectionParameters );
|
||||
initializeFactoryMethod( returnTypeImpl, selectionParameters );
|
||||
if ( factoryMethod != null || canResultTypeFromBeanMappingBeConstructed( returnTypeImpl ) ) {
|
||||
returnTypeToConstruct = returnTypeImpl;
|
||||
}
|
||||
@ -143,7 +159,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
}
|
||||
else if ( isBuilderRequired() ) {
|
||||
returnTypeImpl = returnTypeBuilder.getBuilder();
|
||||
factoryMethod = getFactoryMethod( returnTypeImpl, selectionParameters );
|
||||
initializeFactoryMethod( returnTypeImpl, selectionParameters );
|
||||
if ( factoryMethod != null || canReturnTypeBeConstructed( returnTypeImpl ) ) {
|
||||
returnTypeToConstruct = returnTypeImpl;
|
||||
}
|
||||
@ -153,7 +169,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
}
|
||||
else if ( !method.isUpdateMethod() ) {
|
||||
returnTypeImpl = method.getReturnType();
|
||||
factoryMethod = getFactoryMethod( returnTypeImpl, selectionParameters );
|
||||
initializeFactoryMethod( returnTypeImpl, selectionParameters );
|
||||
if ( factoryMethod != null || canReturnTypeBeConstructed( returnTypeImpl ) ) {
|
||||
returnTypeToConstruct = returnTypeImpl;
|
||||
}
|
||||
@ -171,13 +187,39 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
/* the type that needs to be used in the mapping process as target */
|
||||
Type resultTypeToMap = returnTypeToConstruct == null ? method.getResultType() : returnTypeToConstruct;
|
||||
|
||||
existingVariableNames.addAll( method.getParameterNames() );
|
||||
|
||||
CollectionMappingStrategyGem cms = this.method.getOptions().getMapper().getCollectionMappingStrategy();
|
||||
|
||||
// determine accessors
|
||||
Map<String, Accessor> accessors = resultTypeToMap.getPropertyWriteAccessors( cms );
|
||||
this.targetProperties = accessors.keySet();
|
||||
this.targetProperties = new LinkedHashSet<>( accessors.keySet() );
|
||||
|
||||
this.unprocessedTargetProperties = new LinkedHashMap<>( accessors );
|
||||
|
||||
if ( !method.isUpdateMethod() && !hasFactoryMethod ) {
|
||||
ConstructorAccessor constructorAccessor = getConstructorAccessor( resultTypeToMap );
|
||||
if ( constructorAccessor != null ) {
|
||||
|
||||
this.unprocessedConstructorProperties = constructorAccessor.constructorAccessors;
|
||||
|
||||
factoryMethod = MethodReference.forConstructorInvocation(
|
||||
resultTypeToMap,
|
||||
constructorAccessor.parameterBindings
|
||||
);
|
||||
|
||||
}
|
||||
else {
|
||||
this.unprocessedConstructorProperties = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
this.targetProperties.addAll( this.unprocessedConstructorProperties.keySet() );
|
||||
this.unprocessedTargetProperties.putAll( this.unprocessedConstructorProperties );
|
||||
}
|
||||
else {
|
||||
unprocessedConstructorProperties = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
this.unprocessedSourceProperties = new LinkedHashMap<>();
|
||||
for ( Parameter sourceParameter : method.getSourceParameters() ) {
|
||||
unprocessedSourceParameters.add( sourceParameter );
|
||||
@ -191,7 +233,6 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
unprocessedSourceProperties.put( entry.getKey(), entry.getValue() );
|
||||
}
|
||||
}
|
||||
existingVariableNames.addAll( method.getParameterNames() );
|
||||
|
||||
// get bean mapping (when specified as annotation )
|
||||
if ( beanMapping != null ) {
|
||||
@ -223,6 +264,9 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
// Process the unprocessed defined targets
|
||||
handleUnprocessedDefinedTargets();
|
||||
|
||||
// Initialize unprocessed constructor properties
|
||||
handleUnmappedConstructorProperties();
|
||||
|
||||
// report errors on unmapped properties
|
||||
reportErrorForUnmappedTargetPropertiesIfRequired();
|
||||
reportErrorForUnmappedSourcePropertiesIfRequired();
|
||||
@ -364,6 +408,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
|
||||
if ( propertyMapping != null ) {
|
||||
unprocessedTargetProperties.remove( propertyName );
|
||||
unprocessedConstructorProperties.remove( propertyName );
|
||||
unprocessedSourceProperties.remove( propertyName );
|
||||
iterator.remove();
|
||||
propertyMappings.add( propertyMapping );
|
||||
@ -374,6 +419,28 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
}
|
||||
}
|
||||
|
||||
private void handleUnmappedConstructorProperties() {
|
||||
for ( Entry<String, Accessor> entry : unprocessedConstructorProperties.entrySet() ) {
|
||||
Accessor accessor = entry.getValue();
|
||||
Type accessedType = ctx.getTypeFactory()
|
||||
.getType( accessor.getAccessedType() );
|
||||
String targetPropertyName = entry.getKey();
|
||||
|
||||
propertyMappings.add( new JavaExpressionMappingBuilder()
|
||||
.mappingContext( ctx )
|
||||
.sourceMethod( method )
|
||||
.javaExpression( accessedType.getNull() )
|
||||
.existingVariableNames( existingVariableNames )
|
||||
.target( targetPropertyName, null, accessor )
|
||||
.dependsOn( Collections.emptySet() )
|
||||
.mirror( null )
|
||||
.build()
|
||||
);
|
||||
}
|
||||
|
||||
unprocessedConstructorProperties.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sources the given mappings as per the dependency relationships given via {@code dependsOn()}. If a cycle is
|
||||
* detected, an error is reported.
|
||||
@ -434,7 +501,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
);
|
||||
error = false;
|
||||
}
|
||||
else if ( !resultType.hasEmptyAccessibleConstructor() ) {
|
||||
else if ( !resultType.hasAccessibleConstructor() ) {
|
||||
ctx.getMessager().printMessage(
|
||||
method.getExecutable(),
|
||||
method.getOptions().getBeanMapping().getMirror(),
|
||||
@ -456,7 +523,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
);
|
||||
error = false;
|
||||
}
|
||||
else if ( !returnType.hasEmptyAccessibleConstructor() ) {
|
||||
else if ( !returnType.hasAccessibleConstructor() ) {
|
||||
ctx.getMessager().printMessage(
|
||||
method.getExecutable(),
|
||||
Message.GENERAL_NO_SUITABLE_CONSTRUCTOR,
|
||||
@ -470,20 +537,225 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
/**
|
||||
* Find a factory method for a return type or for a builder.
|
||||
* @param returnTypeImpl the return type implementation to construct
|
||||
* @param selectionParameters
|
||||
* @param @selectionParameters
|
||||
* @return
|
||||
*/
|
||||
private MethodReference getFactoryMethod(Type returnTypeImpl, SelectionParameters selectionParameters) {
|
||||
MethodReference factoryMethod = ObjectFactoryMethodResolver.getFactoryMethod( method,
|
||||
returnTypeImpl,
|
||||
selectionParameters,
|
||||
ctx
|
||||
);
|
||||
if ( factoryMethod == null && returnTypeBuilder != null ) {
|
||||
factoryMethod = ObjectFactoryMethodResolver.getBuilderFactoryMethod( method, returnTypeBuilder );
|
||||
private void initializeFactoryMethod(Type returnTypeImpl, SelectionParameters selectionParameters) {
|
||||
List<SelectedMethod<SourceMethod>> matchingFactoryMethods =
|
||||
ObjectFactoryMethodResolver.getMatchingFactoryMethods(
|
||||
method,
|
||||
returnTypeImpl,
|
||||
selectionParameters,
|
||||
ctx
|
||||
);
|
||||
|
||||
if ( matchingFactoryMethods.isEmpty() ) {
|
||||
if ( factoryMethod == null && returnTypeBuilder != null ) {
|
||||
factoryMethod = ObjectFactoryMethodResolver.getBuilderFactoryMethod( method, returnTypeBuilder );
|
||||
hasFactoryMethod = factoryMethod != null;
|
||||
}
|
||||
}
|
||||
else if ( matchingFactoryMethods.size() == 1 ) {
|
||||
factoryMethod = ObjectFactoryMethodResolver.getFactoryMethodReference(
|
||||
method,
|
||||
first( matchingFactoryMethods ),
|
||||
ctx
|
||||
);
|
||||
hasFactoryMethod = true;
|
||||
}
|
||||
else {
|
||||
ctx.getMessager().printMessage(
|
||||
method.getExecutable(),
|
||||
Message.GENERAL_AMBIGIOUS_FACTORY_METHOD,
|
||||
returnTypeImpl,
|
||||
Strings.join( matchingFactoryMethods, ", " )
|
||||
);
|
||||
hasFactoryMethod = true;
|
||||
}
|
||||
}
|
||||
|
||||
private ConstructorAccessor getConstructorAccessor(Type type) {
|
||||
|
||||
if ( type.isRecord() ) {
|
||||
// If the type is a record then just get the record components and use then
|
||||
List<Element> recordComponents = type.getRecordComponents();
|
||||
List<ParameterBinding> parameterBindings = new ArrayList<>( recordComponents.size() );
|
||||
Map<String, Accessor> constructorAccessors = new LinkedHashMap<>();
|
||||
for ( Element recordComponent : recordComponents ) {
|
||||
String parameterName = recordComponent.getSimpleName().toString();
|
||||
Accessor accessor = createConstructorAccessor( recordComponent, parameterName );
|
||||
constructorAccessors.put(
|
||||
parameterName,
|
||||
accessor
|
||||
);
|
||||
|
||||
parameterBindings.add( ParameterBinding.fromTypeAndName(
|
||||
ctx.getTypeFactory().getType( recordComponent.asType() ),
|
||||
accessor.getSimpleName()
|
||||
) );
|
||||
}
|
||||
return new ConstructorAccessor( parameterBindings, constructorAccessors );
|
||||
}
|
||||
|
||||
return factoryMethod;
|
||||
List<ExecutableElement> constructors = ElementFilter.constructorsIn( type.getTypeElement()
|
||||
.getEnclosedElements() );
|
||||
|
||||
ExecutableElement defaultConstructor = null;
|
||||
List<ExecutableElement> accessibleConstructors = new ArrayList<>( constructors.size() );
|
||||
|
||||
for ( ExecutableElement constructor : constructors ) {
|
||||
if ( constructor.getModifiers().contains( Modifier.PRIVATE ) ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( constructor.getParameters().isEmpty() ) {
|
||||
defaultConstructor = constructor;
|
||||
}
|
||||
else {
|
||||
accessibleConstructors.add( constructor );
|
||||
}
|
||||
}
|
||||
|
||||
if ( defaultConstructor != null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( accessibleConstructors.isEmpty() ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ExecutableElement constructor = null;
|
||||
if ( accessibleConstructors.size() > 1 ) {
|
||||
|
||||
for ( ExecutableElement accessibleConstructor : accessibleConstructors ) {
|
||||
for ( AnnotationMirror annotationMirror : accessibleConstructor.getAnnotationMirrors() ) {
|
||||
if ( annotationMirror.getAnnotationType()
|
||||
.asElement()
|
||||
.getSimpleName()
|
||||
.contentEquals( "Default" ) ) {
|
||||
constructor = accessibleConstructor;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( constructor == null ) {
|
||||
ctx.getMessager().printMessage(
|
||||
method.getExecutable(),
|
||||
GENERAL_AMBIGIOUS_CONSTRUCTORS,
|
||||
type,
|
||||
Strings.join( constructors, ", " )
|
||||
);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
constructor = accessibleConstructors.get( 0 );
|
||||
}
|
||||
|
||||
List<Parameter> constructorParameters = ctx.getTypeFactory()
|
||||
.getParameters( (DeclaredType) type.getTypeMirror(), constructor );
|
||||
|
||||
|
||||
List<String> constructorProperties = null;
|
||||
for ( AnnotationMirror annotationMirror : constructor.getAnnotationMirrors() ) {
|
||||
if ( annotationMirror.getAnnotationType()
|
||||
.asElement()
|
||||
.getSimpleName()
|
||||
.contentEquals( "ConstructorProperties" ) ) {
|
||||
for ( Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : annotationMirror
|
||||
.getElementValues()
|
||||
.entrySet() ) {
|
||||
if ( entry.getKey().getSimpleName().contentEquals( "value" ) ) {
|
||||
constructorProperties = getArrayValues( entry.getValue() );
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( constructorProperties == null ) {
|
||||
Map<String, Accessor> constructorAccessors = new LinkedHashMap<>();
|
||||
List<ParameterBinding> parameterBindings = new ArrayList<>( constructorParameters.size() );
|
||||
for ( Parameter constructorParameter : constructorParameters ) {
|
||||
String parameterName = constructorParameter.getName();
|
||||
Element parameterElement = constructorParameter.getElement();
|
||||
Accessor constructorAccessor = createConstructorAccessor( parameterElement, parameterName );
|
||||
constructorAccessors.put(
|
||||
parameterName,
|
||||
constructorAccessor
|
||||
);
|
||||
parameterBindings.add( ParameterBinding.fromTypeAndName(
|
||||
constructorParameter.getType(),
|
||||
constructorAccessor.getSimpleName()
|
||||
) );
|
||||
}
|
||||
|
||||
return new ConstructorAccessor( parameterBindings, constructorAccessors );
|
||||
}
|
||||
else if ( constructorProperties.size() != constructorParameters.size() ) {
|
||||
ctx.getMessager().printMessage(
|
||||
method.getExecutable(),
|
||||
GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS,
|
||||
type
|
||||
);
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
Map<String, Accessor> constructorAccessors = new LinkedHashMap<>();
|
||||
List<ParameterBinding> parameterBindings = new ArrayList<>( constructorProperties.size() );
|
||||
for ( int i = 0; i < constructorProperties.size(); i++ ) {
|
||||
String parameterName = constructorProperties.get( i );
|
||||
Parameter constructorParameter = constructorParameters.get( i );
|
||||
Element parameterElement = constructorParameter.getElement();
|
||||
Accessor constructorAccessor = createConstructorAccessor( parameterElement, parameterName );
|
||||
constructorAccessors.put(
|
||||
parameterName,
|
||||
constructorAccessor
|
||||
);
|
||||
parameterBindings.add( ParameterBinding.fromTypeAndName(
|
||||
constructorParameter.getType(),
|
||||
constructorAccessor.getSimpleName()
|
||||
) );
|
||||
}
|
||||
|
||||
return new ConstructorAccessor( parameterBindings, constructorAccessors );
|
||||
}
|
||||
}
|
||||
|
||||
private Accessor createConstructorAccessor(Element element, String parameterName) {
|
||||
String safeParameterName = Strings.getSafeVariableName(
|
||||
parameterName,
|
||||
existingVariableNames
|
||||
);
|
||||
existingVariableNames.add( safeParameterName );
|
||||
return new ParameterElementAccessor( element, safeParameterName );
|
||||
}
|
||||
|
||||
private List<String> getArrayValues(AnnotationValue av) {
|
||||
|
||||
if ( av.getValue() instanceof List ) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for ( AnnotationValue v : getValueAsList( av ) ) {
|
||||
Object value = v.getValue();
|
||||
if ( value instanceof String ) {
|
||||
result.add( (String) value );
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private List<AnnotationValue> getValueAsList(AnnotationValue av) {
|
||||
return (List<AnnotationValue>) av.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -531,6 +803,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
// remove the remaining name based properties
|
||||
for ( String handledTarget : handledTargets ) {
|
||||
unprocessedTargetProperties.remove( handledTarget );
|
||||
unprocessedConstructorProperties.remove( handledTarget );
|
||||
unprocessedDefinedTargets.remove( handledTarget );
|
||||
}
|
||||
|
||||
@ -672,7 +945,22 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
// check the mapping options
|
||||
// its an ignored property mapping
|
||||
if ( mapping.isIgnored() ) {
|
||||
propertyMapping = null;
|
||||
if ( targetWriteAccessor != null && targetWriteAccessor.getAccessorType() == AccessorType.PARAMETER ) {
|
||||
// Even though the property is ignored this is a constructor parameter.
|
||||
// Therefore we have to initialize it
|
||||
Type accessedType = ctx.getTypeFactory()
|
||||
.getType( targetWriteAccessor.getAccessedType() );
|
||||
|
||||
propertyMapping = new JavaExpressionMappingBuilder()
|
||||
.mappingContext( ctx )
|
||||
.sourceMethod( method )
|
||||
.javaExpression( accessedType.getNull() )
|
||||
.existingVariableNames( existingVariableNames )
|
||||
.target( targetPropertyName, targetReadAccessor, targetWriteAccessor )
|
||||
.dependsOn( mapping.getDependsOn() )
|
||||
.mirror( mapping.getMirror() )
|
||||
.build();
|
||||
}
|
||||
handledTargets.add( targetPropertyName );
|
||||
}
|
||||
|
||||
@ -821,6 +1109,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
|
||||
String targetPropertyName = sourceRef.getDeepestPropertyName();
|
||||
Accessor targetPropertyWriteAccessor = unprocessedTargetProperties.remove( targetPropertyName );
|
||||
unprocessedConstructorProperties.remove( targetPropertyName );
|
||||
if ( targetPropertyWriteAccessor == null ) {
|
||||
// TODO improve error message
|
||||
ctx.getMessager()
|
||||
@ -1056,6 +1345,18 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
}
|
||||
}
|
||||
|
||||
private static class ConstructorAccessor {
|
||||
private final List<ParameterBinding> parameterBindings;
|
||||
private final Map<String, Accessor> constructorAccessors;
|
||||
|
||||
private ConstructorAccessor(
|
||||
List<ParameterBinding> parameterBindings,
|
||||
Map<String, Accessor> constructorAccessors) {
|
||||
this.parameterBindings = parameterBindings;
|
||||
this.constructorAccessors = constructorAccessors;
|
||||
}
|
||||
}
|
||||
|
||||
private BeanMappingMethod(Method method,
|
||||
Collection<String> existingVariableNames,
|
||||
List<PropertyMapping> propertyMappings,
|
||||
@ -1082,15 +1383,30 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
// intialize constant mappings as all mappings, but take out the ones that can be contributed to a
|
||||
// parameter mapping.
|
||||
this.mappingsByParameter = new HashMap<>();
|
||||
this.constantMappings = new ArrayList<>( propertyMappings );
|
||||
for ( Parameter sourceParameter : getSourceParameters() ) {
|
||||
ArrayList<PropertyMapping> mappingsOfParameter = new ArrayList<>();
|
||||
mappingsByParameter.put( sourceParameter.getName(), mappingsOfParameter );
|
||||
for ( PropertyMapping mapping : propertyMappings ) {
|
||||
if ( sourceParameter.getName().equals( mapping.getSourceBeanName() ) ) {
|
||||
mappingsOfParameter.add( mapping );
|
||||
constantMappings.remove( mapping );
|
||||
this.constantMappings = new ArrayList<>( propertyMappings.size() );
|
||||
this.constructorMappingsByParameter = new LinkedHashMap<>();
|
||||
this.constructorConstantMappings = new ArrayList<>();
|
||||
Set<String> sourceParameterNames = getSourceParameters().stream()
|
||||
.map( Parameter::getName )
|
||||
.collect( Collectors.toSet() );
|
||||
for ( PropertyMapping mapping : propertyMappings ) {
|
||||
if ( mapping.isConstructorMapping() ) {
|
||||
if ( sourceParameterNames.contains( mapping.getSourceBeanName() ) ) {
|
||||
constructorMappingsByParameter.computeIfAbsent(
|
||||
mapping.getSourceBeanName(),
|
||||
key -> new ArrayList<>()
|
||||
).add( mapping );
|
||||
}
|
||||
else {
|
||||
constructorConstantMappings.add( mapping );
|
||||
}
|
||||
}
|
||||
else if ( sourceParameterNames.contains( mapping.getSourceBeanName() ) ) {
|
||||
mappingsByParameter.computeIfAbsent( mapping.getSourceBeanName(), key -> new ArrayList<>() )
|
||||
.add( mapping );
|
||||
}
|
||||
else {
|
||||
constantMappings.add( mapping );
|
||||
}
|
||||
}
|
||||
this.returnTypeToConstruct = returnTypeToConstruct;
|
||||
@ -1100,15 +1416,28 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
|
||||
return constantMappings;
|
||||
}
|
||||
|
||||
public List<PropertyMapping> getConstructorConstantMappings() {
|
||||
return constructorConstantMappings;
|
||||
}
|
||||
|
||||
public List<PropertyMapping> propertyMappingsByParameter(Parameter parameter) {
|
||||
// issues: #909 and #1244. FreeMarker has problem getting values from a map when the search key is size or value
|
||||
return mappingsByParameter.get( parameter.getName() );
|
||||
return mappingsByParameter.getOrDefault( parameter.getName(), Collections.emptyList() );
|
||||
}
|
||||
|
||||
public List<PropertyMapping> constructorPropertyMappingsByParameter(Parameter parameter) {
|
||||
// issues: #909 and #1244. FreeMarker has problem getting values from a map when the search key is size or value
|
||||
return constructorMappingsByParameter.getOrDefault( parameter.getName(), Collections.emptyList() );
|
||||
}
|
||||
|
||||
public Type getReturnTypeToConstruct() {
|
||||
return returnTypeToConstruct;
|
||||
}
|
||||
|
||||
public boolean hasConstructorMappings() {
|
||||
return !constructorMappingsByParameter.isEmpty() || !constructorConstantMappings.isEmpty();
|
||||
}
|
||||
|
||||
public MethodReference getFinalizerMethod() {
|
||||
return finalizerMethod;
|
||||
}
|
||||
|
@ -130,7 +130,7 @@ public class CollectionAssignmentBuilder {
|
||||
CollectionMappingStrategyGem cms = method.getOptions().getMapper().getCollectionMappingStrategy();
|
||||
boolean targetImmutable = cms == CollectionMappingStrategyGem.TARGET_IMMUTABLE || targetReadAccessor == null;
|
||||
|
||||
if ( targetAccessorType == AccessorType.SETTER || targetAccessorType == AccessorType.FIELD ) {
|
||||
if ( targetAccessorType == AccessorType.SETTER || targetAccessorType.isFieldAssignment() ) {
|
||||
|
||||
if ( result.isCallingUpdateMethod() && !targetImmutable ) {
|
||||
|
||||
@ -149,7 +149,7 @@ public class CollectionAssignmentBuilder {
|
||||
result,
|
||||
method.getThrownTypes(),
|
||||
factoryMethod,
|
||||
targetAccessorType == AccessorType.FIELD,
|
||||
targetAccessorType.isFieldAssignment(),
|
||||
targetType,
|
||||
true,
|
||||
nvpms == SET_TO_NULL && !targetType.isPrimitive(),
|
||||
@ -165,7 +165,7 @@ public class CollectionAssignmentBuilder {
|
||||
nvcs,
|
||||
nvpms,
|
||||
ctx.getTypeFactory(),
|
||||
targetAccessorType == AccessorType.FIELD
|
||||
targetAccessorType.isFieldAssignment()
|
||||
);
|
||||
}
|
||||
else if ( result.getType() == Assignment.AssignmentType.DIRECT ||
|
||||
@ -176,16 +176,18 @@ public class CollectionAssignmentBuilder {
|
||||
method.getThrownTypes(),
|
||||
targetType,
|
||||
ctx.getTypeFactory(),
|
||||
targetAccessorType == AccessorType.FIELD
|
||||
targetAccessorType.isFieldAssignment()
|
||||
);
|
||||
}
|
||||
else {
|
||||
//TODO init default value
|
||||
|
||||
// target accessor is setter, so wrap the setter in setter map/ collection handling
|
||||
result = new SetterWrapperForCollectionsAndMaps(
|
||||
result,
|
||||
method.getThrownTypes(),
|
||||
targetType,
|
||||
targetAccessorType == AccessorType.FIELD
|
||||
targetAccessorType.isFieldAssignment()
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -203,7 +205,7 @@ public class CollectionAssignmentBuilder {
|
||||
result,
|
||||
method.getThrownTypes(),
|
||||
targetType,
|
||||
targetAccessorType == AccessorType.FIELD
|
||||
targetAccessorType.isFieldAssignment()
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -8,6 +8,7 @@ package org.mapstruct.ap.internal.model;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@ -57,6 +58,7 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
private final List<ParameterBinding> parameterBindings;
|
||||
private final Parameter providingParameter;
|
||||
private final boolean isStatic;
|
||||
private final boolean isConstructor;
|
||||
|
||||
/**
|
||||
* Creates a new reference to the given method.
|
||||
@ -91,6 +93,7 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
this.definingType = method.getDefiningType();
|
||||
this.isStatic = method.isStatic();
|
||||
this.name = method.getName();
|
||||
this.isConstructor = false;
|
||||
}
|
||||
|
||||
private MethodReference(BuiltInMethod method, ConversionContext contextParam) {
|
||||
@ -106,6 +109,7 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
this.parameterBindings = ParameterBinding.fromParameters( method.getParameters() );
|
||||
this.isStatic = method.isStatic();
|
||||
this.name = method.getName();
|
||||
this.isConstructor = false;
|
||||
}
|
||||
|
||||
private MethodReference(String name, Type definingType, boolean isStatic) {
|
||||
@ -121,6 +125,37 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
this.parameterBindings = Collections.emptyList();
|
||||
this.providingParameter = null;
|
||||
this.isStatic = isStatic;
|
||||
this.isConstructor = false;
|
||||
}
|
||||
|
||||
private MethodReference(Type definingType, List<ParameterBinding> parameterBindings) {
|
||||
this.name = null;
|
||||
this.definingType = definingType;
|
||||
this.sourceParameters = Collections.emptyList();
|
||||
this.returnType = null;
|
||||
this.declaringMapper = null;
|
||||
this.thrownTypes = Collections.emptyList();
|
||||
this.isUpdateMethod = false;
|
||||
this.contextParam = null;
|
||||
this.parameterBindings = parameterBindings;
|
||||
this.providingParameter = null;
|
||||
this.isStatic = false;
|
||||
this.isConstructor = true;
|
||||
|
||||
if ( parameterBindings.isEmpty() ) {
|
||||
this.importTypes = Collections.emptySet();
|
||||
}
|
||||
else {
|
||||
Set<Type> imported = new LinkedHashSet<>();
|
||||
|
||||
for ( ParameterBinding binding : parameterBindings ) {
|
||||
imported.add( binding.getType() );
|
||||
}
|
||||
|
||||
imported.add( definingType );
|
||||
|
||||
this.importTypes = Collections.unmodifiableSet( imported );
|
||||
}
|
||||
}
|
||||
|
||||
public MapperReference getDeclaringMapper() {
|
||||
@ -271,6 +306,10 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
return isStatic;
|
||||
}
|
||||
|
||||
public boolean isConstructor() {
|
||||
return isConstructor;
|
||||
}
|
||||
|
||||
public List<ParameterBinding> getParameterBindings() {
|
||||
return parameterBindings;
|
||||
}
|
||||
@ -341,6 +380,10 @@ public class MethodReference extends ModelElement implements Assignment {
|
||||
return new MethodReference( methodName, null, false );
|
||||
}
|
||||
|
||||
public static MethodReference forConstructorInvocation(Type type, List<ParameterBinding> parameterBindings) {
|
||||
return new MethodReference( type, parameterBindings );
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
String mapper = declaringMapper != null ? declaringMapper.getType().getName() : "";
|
||||
|
@ -42,6 +42,9 @@ public abstract class NormalTypeMappingMethod extends MappingMethod {
|
||||
types.addAll( getReturnType().getImplementationType().getImportTypes() );
|
||||
}
|
||||
}
|
||||
else if ( factoryMethod != null ) {
|
||||
types.addAll( factoryMethod.getImportTypes() );
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
|
@ -67,16 +67,12 @@ public class ObjectFactoryMethodResolver {
|
||||
MappingBuilderContext ctx) {
|
||||
|
||||
|
||||
MethodSelectors selectors =
|
||||
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory(), ctx.getMessager() );
|
||||
|
||||
List<SelectedMethod<SourceMethod>> matchingFactoryMethods =
|
||||
selectors.getMatchingMethods(
|
||||
method,
|
||||
getAllAvailableMethods( method, ctx.getSourceModel() ),
|
||||
java.util.Collections.emptyList(),
|
||||
alternativeTarget,
|
||||
SelectionCriteria.forFactoryMethods( selectionParameters ) );
|
||||
List<SelectedMethod<SourceMethod>> matchingFactoryMethods = getMatchingFactoryMethods(
|
||||
method,
|
||||
alternativeTarget,
|
||||
selectionParameters,
|
||||
ctx
|
||||
);
|
||||
|
||||
if (matchingFactoryMethods.isEmpty()) {
|
||||
return null;
|
||||
@ -94,6 +90,11 @@ public class ObjectFactoryMethodResolver {
|
||||
|
||||
SelectedMethod<SourceMethod> matchingFactoryMethod = first( matchingFactoryMethods );
|
||||
|
||||
return getFactoryMethodReference( method, matchingFactoryMethod, ctx );
|
||||
}
|
||||
|
||||
public static MethodReference getFactoryMethodReference(Method method,
|
||||
SelectedMethod<SourceMethod> matchingFactoryMethod, MappingBuilderContext ctx) {
|
||||
Parameter providingParameter =
|
||||
method.getContextProvidedMethods().getParameterForProvidedMethod( matchingFactoryMethod.getMethod() );
|
||||
|
||||
@ -115,6 +116,22 @@ public class ObjectFactoryMethodResolver {
|
||||
}
|
||||
}
|
||||
|
||||
public static List<SelectedMethod<SourceMethod>> getMatchingFactoryMethods( Method method,
|
||||
Type alternativeTarget,
|
||||
SelectionParameters selectionParameters,
|
||||
MappingBuilderContext ctx) {
|
||||
|
||||
MethodSelectors selectors =
|
||||
new MethodSelectors( ctx.getTypeUtils(), ctx.getElementUtils(), ctx.getTypeFactory(), ctx.getMessager() );
|
||||
|
||||
return selectors.getMatchingMethods(
|
||||
method,
|
||||
getAllAvailableMethods( method, ctx.getSourceModel() ),
|
||||
java.util.Collections.emptyList(),
|
||||
alternativeTarget,
|
||||
SelectionCriteria.forFactoryMethods( selectionParameters ) );
|
||||
}
|
||||
|
||||
public static MethodReference getBuilderFactoryMethod(Method method, BuilderType builder ) {
|
||||
if ( builder == null ) {
|
||||
return null;
|
||||
|
@ -70,6 +70,7 @@ public class PropertyMapping extends ModelElement {
|
||||
private final Assignment assignment;
|
||||
private final Set<String> dependsOn;
|
||||
private final Assignment defaultValueAssignment;
|
||||
private final boolean constructorMapping;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static class MappingBuilderBase<T extends MappingBuilderBase<T>> extends AbstractBaseBuilder<T> {
|
||||
@ -126,7 +127,7 @@ public class PropertyMapping extends ModelElement {
|
||||
}
|
||||
|
||||
protected boolean isFieldAssignment() {
|
||||
return targetWriteAccessorType == AccessorType.FIELD;
|
||||
return targetWriteAccessorType.isFieldAssignment();
|
||||
}
|
||||
}
|
||||
|
||||
@ -278,7 +279,8 @@ public class PropertyMapping extends ModelElement {
|
||||
targetType,
|
||||
assignment,
|
||||
dependsOn,
|
||||
getDefaultValueAssignment( assignment )
|
||||
getDefaultValueAssignment( assignment ),
|
||||
targetWriteAccessorType == AccessorType.PARAMETER
|
||||
);
|
||||
}
|
||||
|
||||
@ -376,7 +378,7 @@ public class PropertyMapping extends ModelElement {
|
||||
|
||||
Assignment result;
|
||||
|
||||
if ( targetAccessorType == AccessorType.SETTER || targetAccessorType == AccessorType.FIELD ) {
|
||||
if ( targetAccessorType == AccessorType.SETTER || targetAccessorType.isFieldAssignment() ) {
|
||||
result = assignToPlainViaSetter( targetType, rightHandSide );
|
||||
}
|
||||
else {
|
||||
@ -473,6 +475,7 @@ public class PropertyMapping extends ModelElement {
|
||||
private Assignment assignToArray(Type targetType, Assignment rightHandSide) {
|
||||
|
||||
Type arrayType = ctx.getTypeFactory().getType( Arrays.class );
|
||||
//TODO init default value
|
||||
return new ArrayCopyWrapper(
|
||||
rightHandSide,
|
||||
targetPropertyName,
|
||||
@ -803,7 +806,7 @@ public class PropertyMapping extends ModelElement {
|
||||
if ( assignment != null ) {
|
||||
|
||||
if ( targetWriteAccessor.getAccessorType() == AccessorType.SETTER ||
|
||||
targetWriteAccessor.getAccessorType() == AccessorType.FIELD ) {
|
||||
targetWriteAccessor.getAccessorType().isFieldAssignment() ) {
|
||||
|
||||
// target accessor is setter, so decorate assignment as setter
|
||||
if ( assignment.isCallingUpdateMethod() ) {
|
||||
@ -874,7 +877,8 @@ public class PropertyMapping extends ModelElement {
|
||||
targetType,
|
||||
assignment,
|
||||
dependsOn,
|
||||
null
|
||||
null,
|
||||
targetWriteAccessorType == AccessorType.PARAMETER
|
||||
);
|
||||
}
|
||||
|
||||
@ -919,7 +923,7 @@ public class PropertyMapping extends ModelElement {
|
||||
Assignment assignment = new SourceRHS( javaExpression, null, existingVariableNames, "" );
|
||||
|
||||
if ( targetWriteAccessor.getAccessorType() == AccessorType.SETTER ||
|
||||
targetWriteAccessor.getAccessorType() == AccessorType.FIELD ) {
|
||||
targetWriteAccessor.getAccessorType().isFieldAssignment() ) {
|
||||
// setter, so wrap in setter
|
||||
assignment = new SetterWrapper( assignment, method.getThrownTypes(), isFieldAssignment() );
|
||||
}
|
||||
@ -939,7 +943,8 @@ public class PropertyMapping extends ModelElement {
|
||||
targetType,
|
||||
assignment,
|
||||
dependsOn,
|
||||
null
|
||||
null,
|
||||
targetWriteAccessorType == AccessorType.PARAMETER
|
||||
);
|
||||
}
|
||||
|
||||
@ -947,18 +952,19 @@ public class PropertyMapping extends ModelElement {
|
||||
|
||||
// Constructor for creating mappings of constant expressions.
|
||||
private PropertyMapping(String name, String targetWriteAccessorName,
|
||||
ValueProvider targetReadAccessorProvider,
|
||||
Type targetType, Assignment propertyAssignment,
|
||||
Set<String> dependsOn, Assignment defaultValueAssignment ) {
|
||||
ValueProvider targetReadAccessorProvider,
|
||||
Type targetType, Assignment propertyAssignment,
|
||||
Set<String> dependsOn, Assignment defaultValueAssignment, boolean constructorMapping) {
|
||||
this( name, null, targetWriteAccessorName, targetReadAccessorProvider,
|
||||
targetType, propertyAssignment, dependsOn, defaultValueAssignment
|
||||
targetType, propertyAssignment, dependsOn, defaultValueAssignment,
|
||||
constructorMapping
|
||||
);
|
||||
}
|
||||
|
||||
private PropertyMapping(String name, String sourceBeanName, String targetWriteAccessorName,
|
||||
ValueProvider targetReadAccessorProvider, Type targetType,
|
||||
Assignment assignment,
|
||||
Set<String> dependsOn, Assignment defaultValueAssignment) {
|
||||
ValueProvider targetReadAccessorProvider, Type targetType,
|
||||
Assignment assignment,
|
||||
Set<String> dependsOn, Assignment defaultValueAssignment, boolean constructorMapping) {
|
||||
this.name = name;
|
||||
this.sourceBeanName = sourceBeanName;
|
||||
this.targetWriteAccessorName = targetWriteAccessorName;
|
||||
@ -968,6 +974,7 @@ public class PropertyMapping extends ModelElement {
|
||||
this.assignment = assignment;
|
||||
this.dependsOn = dependsOn != null ? dependsOn : Collections.<String>emptySet();
|
||||
this.defaultValueAssignment = defaultValueAssignment;
|
||||
this.constructorMapping = constructorMapping;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1001,6 +1008,10 @@ public class PropertyMapping extends ModelElement {
|
||||
return defaultValueAssignment;
|
||||
}
|
||||
|
||||
public boolean isConstructorMapping() {
|
||||
return constructorMapping;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Type> getImportTypes() {
|
||||
if ( defaultValueAssignment == null ) {
|
||||
|
@ -9,6 +9,7 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
|
||||
import org.mapstruct.ap.internal.gem.ContextGem;
|
||||
@ -23,6 +24,7 @@ import org.mapstruct.ap.internal.util.Collections;
|
||||
*/
|
||||
public class Parameter extends ModelElement {
|
||||
|
||||
private final Element element;
|
||||
private final String name;
|
||||
private final String originalName;
|
||||
private final Type type;
|
||||
@ -32,8 +34,20 @@ public class Parameter extends ModelElement {
|
||||
|
||||
private final boolean varArgs;
|
||||
|
||||
private Parameter(Element element, Type type, boolean varArgs) {
|
||||
this.element = element;
|
||||
this.name = element.getSimpleName().toString();
|
||||
this.originalName = name;
|
||||
this.type = type;
|
||||
this.mappingTarget = MappingTargetGem.instanceOn( element ) != null;
|
||||
this.targetType = TargetTypeGem.instanceOn( element ) != null;
|
||||
this.mappingContext = ContextGem.instanceOn( element ) != null;
|
||||
this.varArgs = varArgs;
|
||||
}
|
||||
|
||||
private Parameter(String name, Type type, boolean mappingTarget, boolean targetType, boolean mappingContext,
|
||||
boolean varArgs) {
|
||||
this.element = null;
|
||||
this.name = name;
|
||||
this.originalName = name;
|
||||
this.type = type;
|
||||
@ -47,6 +61,10 @@ public class Parameter extends ModelElement {
|
||||
this( name, type, false, false, false, false );
|
||||
}
|
||||
|
||||
public Element getElement() {
|
||||
return element;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@ -115,11 +133,8 @@ public class Parameter extends ModelElement {
|
||||
|
||||
public static Parameter forElementAndType(VariableElement element, Type parameterType, boolean isVarArgs) {
|
||||
return new Parameter(
|
||||
element.getSimpleName().toString(),
|
||||
element,
|
||||
parameterType,
|
||||
MappingTargetGem.instanceOn( element ) != null,
|
||||
TargetTypeGem.instanceOn( element ) != null,
|
||||
ContextGem.instanceOn( element ) != null,
|
||||
isVarArgs
|
||||
);
|
||||
}
|
||||
|
@ -111,6 +111,17 @@ public class ParameterBinding {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ParameterBinding fromTypeAndName(Type parameterType, String parameterName) {
|
||||
return new ParameterBinding(
|
||||
parameterType,
|
||||
parameterName,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
null
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param classTypeOf the type representing {@code Class<X>} for the target type {@code X}
|
||||
* @return a parameter binding representing a target type parameter
|
||||
|
@ -90,14 +90,16 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
|
||||
private List<ExecutableElement> allMethods = null;
|
||||
private List<VariableElement> allFields = null;
|
||||
private List<Element> recordComponents = null;
|
||||
|
||||
private List<Accessor> setters = null;
|
||||
private List<Accessor> adders = null;
|
||||
private List<Accessor> alternativeTargetAccessors = null;
|
||||
private Map<String, Accessor> constructorAccessors = null;
|
||||
|
||||
private Type boundingBase = null;
|
||||
|
||||
private Boolean hasEmptyAccessibleConstructor;
|
||||
private Boolean hasAccessibleConstructor;
|
||||
|
||||
private final Filters filters;
|
||||
|
||||
@ -290,6 +292,10 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
return packageName != null && packageName.startsWith( "java." );
|
||||
}
|
||||
|
||||
public boolean isRecord() {
|
||||
return typeElement.getKind().name().equals( "RECORD" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this type is a sub-type of {@link java.util.stream.Stream}.
|
||||
*
|
||||
@ -498,7 +504,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Accessor> recordAccessors = filters.recordsIn( typeElement );
|
||||
Map<String, Accessor> recordAccessors = filters.recordAccessorsIn( getRecordComponents() );
|
||||
for ( Map.Entry<String, Accessor> recordEntry : recordAccessors.entrySet() ) {
|
||||
modifiableGetters.putIfAbsent( recordEntry.getKey(), recordEntry.getValue() );
|
||||
}
|
||||
@ -600,6 +606,14 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
return result;
|
||||
}
|
||||
|
||||
public List<Element> getRecordComponents() {
|
||||
if ( recordComponents == null ) {
|
||||
recordComponents = filters.recordComponentsIn( typeElement );
|
||||
}
|
||||
|
||||
return recordComponents;
|
||||
}
|
||||
|
||||
private Type determinePreferredType(Accessor readAccessor) {
|
||||
if ( readAccessor != null ) {
|
||||
return typeFactory.getReturnType( (DeclaredType) typeMirror, readAccessor );
|
||||
@ -613,7 +627,7 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
return parameter.getType();
|
||||
}
|
||||
else if ( candidate.getAccessorType() == AccessorType.GETTER
|
||||
|| candidate.getAccessorType() == AccessorType.FIELD ) {
|
||||
|| candidate.getAccessorType().isFieldAssignment() ) {
|
||||
return typeFactory.getReturnType( (DeclaredType) typeMirror, candidate );
|
||||
}
|
||||
return null;
|
||||
@ -636,11 +650,12 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
}
|
||||
|
||||
private String getPropertyName(Accessor accessor ) {
|
||||
if ( accessor.getAccessorType() == AccessorType.FIELD ) {
|
||||
return accessorNaming.getPropertyName( (VariableElement) accessor.getElement() );
|
||||
Element accessorElement = accessor.getElement();
|
||||
if ( accessorElement instanceof ExecutableElement ) {
|
||||
return accessorNaming.getPropertyName( (ExecutableElement) accessorElement );
|
||||
}
|
||||
else {
|
||||
return accessorNaming.getPropertyName( (ExecutableElement) accessor.getElement() );
|
||||
return accessor.getSimpleName();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1009,20 +1024,18 @@ public class Type extends ModelElement implements Comparable<Type> {
|
||||
return boundingBase;
|
||||
}
|
||||
|
||||
public boolean hasEmptyAccessibleConstructor() {
|
||||
|
||||
if ( this.hasEmptyAccessibleConstructor == null ) {
|
||||
hasEmptyAccessibleConstructor = false;
|
||||
public boolean hasAccessibleConstructor() {
|
||||
if ( hasAccessibleConstructor == null ) {
|
||||
hasAccessibleConstructor = false;
|
||||
List<ExecutableElement> constructors = ElementFilter.constructorsIn( typeElement.getEnclosedElements() );
|
||||
for ( ExecutableElement constructor : constructors ) {
|
||||
if ( !constructor.getModifiers().contains( Modifier.PRIVATE )
|
||||
&& constructor.getParameters().isEmpty() ) {
|
||||
hasEmptyAccessibleConstructor = true;
|
||||
if ( !constructor.getModifiers().contains( Modifier.PRIVATE ) ) {
|
||||
hasAccessibleConstructor = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return hasEmptyAccessibleConstructor;
|
||||
return hasAccessibleConstructor;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -50,7 +50,6 @@ import org.mapstruct.ap.internal.util.NativeTypes;
|
||||
import org.mapstruct.ap.internal.util.RoundContext;
|
||||
import org.mapstruct.ap.internal.util.Strings;
|
||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||
import org.mapstruct.ap.internal.util.accessor.AccessorType;
|
||||
import org.mapstruct.ap.spi.AstModifyingAnnotationProcessor;
|
||||
import org.mapstruct.ap.spi.BuilderInfo;
|
||||
import org.mapstruct.ap.spi.MoreThanOneBuilderCreationMethodException;
|
||||
@ -360,7 +359,7 @@ public class TypeFactory {
|
||||
}
|
||||
|
||||
public Parameter getSingleParameter(DeclaredType includingType, Accessor method) {
|
||||
if ( method.getAccessorType() == AccessorType.FIELD ) {
|
||||
if ( method.getAccessorType().isFieldAssignment() ) {
|
||||
return null;
|
||||
}
|
||||
ExecutableElement executable = (ExecutableElement) method.getElement();
|
||||
@ -376,11 +375,15 @@ public class TypeFactory {
|
||||
|
||||
public List<Parameter> getParameters(DeclaredType includingType, Accessor accessor) {
|
||||
ExecutableElement method = (ExecutableElement) accessor.getElement();
|
||||
TypeMirror methodType = getMethodType( includingType, accessor.getElement() );
|
||||
return getParameters( includingType, method );
|
||||
}
|
||||
|
||||
public List<Parameter> getParameters(DeclaredType includingType, ExecutableElement method) {
|
||||
ExecutableType methodType = getMethodType( includingType, method );
|
||||
if ( method == null || methodType.getKind() != TypeKind.EXECUTABLE ) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return getParameters( (ExecutableType) methodType, method );
|
||||
return getParameters( methodType, method );
|
||||
}
|
||||
|
||||
public List<Parameter> getParameters(ExecutableType methodType, ExecutableElement method) {
|
||||
@ -433,7 +436,7 @@ public class TypeFactory {
|
||||
}
|
||||
|
||||
public List<Type> getThrownTypes(Accessor accessor) {
|
||||
if (accessor.getAccessorType() == AccessorType.FIELD) {
|
||||
if (accessor.getAccessorType().isFieldAssignment()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
return extractTypes( ( (ExecutableElement) accessor.getElement() ).getThrownTypes() );
|
||||
|
@ -7,7 +7,6 @@ package org.mapstruct.ap.internal.util;
|
||||
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeKind;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
@ -68,10 +67,6 @@ public final class AccessorNamingUtils {
|
||||
return accessorNamingStrategy.getPropertyName( executable );
|
||||
}
|
||||
|
||||
public String getPropertyName(VariableElement variable) {
|
||||
return variable.getSimpleName().toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param adderMethod the adder method
|
||||
*
|
||||
|
@ -7,6 +7,7 @@ package org.mapstruct.ap.internal.util;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -23,7 +24,7 @@ import javax.lang.model.util.Types;
|
||||
|
||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||
import org.mapstruct.ap.internal.util.accessor.ExecutableElementAccessor;
|
||||
import org.mapstruct.ap.internal.util.accessor.VariableElementAccessor;
|
||||
import org.mapstruct.ap.internal.util.accessor.FieldElementAccessor;
|
||||
|
||||
import static org.mapstruct.ap.internal.util.Collections.first;
|
||||
import static org.mapstruct.ap.internal.util.accessor.AccessorType.ADDER;
|
||||
@ -75,13 +76,25 @@ public class Filters {
|
||||
.collect( Collectors.toCollection( LinkedList::new ) );
|
||||
}
|
||||
|
||||
public Map<String, Accessor> recordsIn(TypeElement typeElement) {
|
||||
if ( RECORD_COMPONENTS_METHOD == null || RECORD_COMPONENT_ACCESSOR_METHOD == null ) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<Element> recordComponentsIn(TypeElement typeElement) {
|
||||
if ( RECORD_COMPONENTS_METHOD == null ) {
|
||||
return java.util.Collections.emptyList();
|
||||
}
|
||||
|
||||
try {
|
||||
return (List<Element>) RECORD_COMPONENTS_METHOD.invoke( typeElement );
|
||||
}
|
||||
catch ( IllegalAccessException | InvocationTargetException e ) {
|
||||
return java.util.Collections.emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, Accessor> recordAccessorsIn(Collection<Element> recordComponents) {
|
||||
if ( RECORD_COMPONENT_ACCESSOR_METHOD == null ) {
|
||||
return java.util.Collections.emptyMap();
|
||||
}
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
List<Element> recordComponents = (List<Element>) RECORD_COMPONENTS_METHOD.invoke( typeElement );
|
||||
Map<String, Accessor> recordAccessors = new LinkedHashMap<>();
|
||||
for ( Element recordComponent : recordComponents ) {
|
||||
ExecutableElement recordExecutableElement =
|
||||
@ -109,7 +122,7 @@ public class Filters {
|
||||
public List<Accessor> fieldsIn(List<VariableElement> accessors) {
|
||||
return accessors.stream()
|
||||
.filter( Fields::isFieldAccessor )
|
||||
.map( VariableElementAccessor::new )
|
||||
.map( FieldElementAccessor::new )
|
||||
.collect( Collectors.toCollection( LinkedList::new ) );
|
||||
}
|
||||
|
||||
|
@ -110,12 +110,14 @@ public enum Message {
|
||||
GENERAL_ABSTRACT_RETURN_TYPE( "The return type %s is an abstract class or interface. Provide a non abstract / non interface result type or a factory method." ),
|
||||
GENERAL_AMBIGIOUS_MAPPING_METHOD( "Ambiguous mapping methods found for mapping %s to %s: %s." ),
|
||||
GENERAL_AMBIGIOUS_FACTORY_METHOD( "Ambiguous factory methods found for creating %s: %s." ),
|
||||
GENERAL_AMBIGIOUS_CONSTRUCTORS( "Ambiguous constructors found for creating %s. Either declare parameterless constructor or annotate the default constructor with an annotation named @Default." ),
|
||||
GENERAL_CONSTRUCTOR_PROPERTIES_NOT_MATCHING_PARAMETERS( "Incorrect @ConstructorProperties for %s. The size of the @ConstructorProperties does not match the number of constructor parameters" ),
|
||||
GENERAL_UNSUPPORTED_DATE_FORMAT_CHECK( "No dateFormat check is supported for types %s, %s" ),
|
||||
GENERAL_VALID_DATE( "Given date format \"%s\" is valid.", Diagnostic.Kind.NOTE ),
|
||||
GENERAL_INVALID_DATE( "Given date format \"%s\" is invalid. Message: \"%s\"." ),
|
||||
GENERAL_JODA_NOT_ON_CLASSPATH( "Cannot validate Joda dateformat, no Joda on classpath. Consider adding Joda to the annotation processorpath.", Diagnostic.Kind.WARNING ),
|
||||
GENERAL_NOT_ALL_FORGED_CREATED( "Internal Error in creation of Forged Methods, it was expected all Forged Methods to finished with creation, but %s did not" ),
|
||||
GENERAL_NO_SUITABLE_CONSTRUCTOR( "%s does not have an accessible parameterless constructor." ),
|
||||
GENERAL_NO_SUITABLE_CONSTRUCTOR( "%s does not have an accessible constructor." ),
|
||||
GENERAL_NO_QUALIFYING_METHOD( "No qualifying method found for qualifiers: %s and / or qualifying names: %s" ),
|
||||
|
||||
BUILDER_MORE_THAN_ONE_BUILDER_CREATION_METHOD( "More than one builder creation method for \"%s\". Found methods: \"%s\". Builder will not be used. Consider implementing a custom BuilderProvider SPI.", Diagnostic.Kind.WARNING ),
|
||||
|
@ -6,7 +6,6 @@
|
||||
package org.mapstruct.ap.internal.util;
|
||||
|
||||
import org.mapstruct.ap.internal.util.accessor.Accessor;
|
||||
import org.mapstruct.ap.internal.util.accessor.AccessorType;
|
||||
|
||||
/**
|
||||
* This a wrapper class which provides the value that needs to be used in the models.
|
||||
@ -46,7 +45,7 @@ public class ValueProvider {
|
||||
return null;
|
||||
}
|
||||
String value = accessor.getSimpleName();
|
||||
if ( accessor.getAccessorType() != AccessorType.FIELD ) {
|
||||
if ( !accessor.getAccessorType().isFieldAssignment() ) {
|
||||
value += "()";
|
||||
}
|
||||
return new ValueProvider( value );
|
||||
|
@ -8,9 +8,8 @@ package org.mapstruct.ap.internal.util.accessor;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ExecutableElement;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.element.Modifier;
|
||||
import javax.lang.model.element.Name;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
/**
|
||||
|
@ -7,9 +7,14 @@ package org.mapstruct.ap.internal.util.accessor;
|
||||
|
||||
public enum AccessorType {
|
||||
|
||||
PARAMETER,
|
||||
FIELD,
|
||||
GETTER,
|
||||
SETTER,
|
||||
ADDER,
|
||||
PRESENCE_CHECKER;
|
||||
|
||||
public boolean isFieldAssignment() {
|
||||
return this == FIELD || this == PARAMETER;
|
||||
}
|
||||
}
|
||||
|
@ -13,9 +13,9 @@ import javax.lang.model.type.TypeMirror;
|
||||
*
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class VariableElementAccessor extends AbstractAccessor<VariableElement> {
|
||||
public class FieldElementAccessor extends AbstractAccessor<VariableElement> {
|
||||
|
||||
public VariableElementAccessor(VariableElement element) {
|
||||
public FieldElementAccessor(VariableElement element) {
|
||||
super( element );
|
||||
}
|
||||
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.internal.util.accessor;
|
||||
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.VariableElement;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
/**
|
||||
* An {@link Accessor} that wraps a {@link VariableElement}.
|
||||
*
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class ParameterElementAccessor extends AbstractAccessor<Element> {
|
||||
|
||||
protected final String name;
|
||||
|
||||
public ParameterElementAccessor(Element element, String name) {
|
||||
super( element );
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSimpleName() {
|
||||
return name != null ? name : super.getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TypeMirror getAccessedType() {
|
||||
return element.asType();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return element.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessorType getAccessorType() {
|
||||
return AccessorType.PARAMETER;
|
||||
}
|
||||
|
||||
}
|
@ -25,7 +25,62 @@
|
||||
</#if>
|
||||
|
||||
<#if !existingInstanceMapping>
|
||||
<@includeModel object=returnTypeToConstruct/> ${resultName} = <#if factoryMethod??><@includeModel object=factoryMethod targetType=returnTypeToConstruct/><#else>new <@includeModel object=returnTypeToConstruct/>()</#if>;
|
||||
<#if hasConstructorMappings()>
|
||||
<#if (sourceParameters?size > 1)>
|
||||
<#list sourceParametersExcludingPrimitives as sourceParam>
|
||||
<#if (constructorPropertyMappingsByParameter(sourceParam)?size > 0)>
|
||||
<#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping>
|
||||
<@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName};
|
||||
</#list>
|
||||
if ( ${sourceParam.name} != null ) {
|
||||
<#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping>
|
||||
<@includeModel object=propertyMapping existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/>
|
||||
</#list>
|
||||
}
|
||||
else {
|
||||
<#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping>
|
||||
${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null};
|
||||
</#list>
|
||||
}
|
||||
</#if>
|
||||
</#list>
|
||||
<#list sourcePrimitiveParameters as sourceParam>
|
||||
<#if (constructorPropertyMappingsByParameter(sourceParam)?size > 0)>
|
||||
<#list constructorPropertyMappingsByParameter(sourceParam) as propertyMapping>
|
||||
<@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName};
|
||||
<@includeModel object=propertyMapping existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/>
|
||||
</#list>
|
||||
</#if>
|
||||
</#list>
|
||||
<#else>
|
||||
<#list constructorPropertyMappingsByParameter(sourceParameters[0]) as propertyMapping>
|
||||
<@includeModel object=propertyMapping.targetType /> ${propertyMapping.targetWriteAccessorName};
|
||||
</#list>
|
||||
<#if mapNullToDefault>if ( ${sourceParameters[0].name} != null ) {</#if>
|
||||
<#list constructorPropertyMappingsByParameter(sourceParameters[0]) as propertyMapping>
|
||||
<@includeModel object=propertyMapping existingInstanceMapping=existingInstanceMapping defaultValueAssignment=propertyMapping.defaultValueAssignment/>
|
||||
</#list>
|
||||
<#if mapNullToDefault>
|
||||
}
|
||||
else {
|
||||
<#list constructorPropertyMappingsByParameter(sourceParameters[0]) as propertyMapping>
|
||||
${propertyMapping.targetWriteAccessorName} = ${propertyMapping.targetType.null};
|
||||
</#list>
|
||||
}
|
||||
</#if>
|
||||
</#if>
|
||||
<#list constructorConstantMappings as constantMapping>
|
||||
|
||||
<@compress single_line=true>
|
||||
<@includeModel object=constantMapping.targetType /> <@includeModel object=constantMapping existingInstanceMapping=existingInstanceMapping/>
|
||||
</@compress>
|
||||
</#list>
|
||||
|
||||
|
||||
<@includeModel object=returnTypeToConstruct/> ${resultName} = <@includeModel object=factoryMethod targetType=returnTypeToConstruct/>;
|
||||
<#else >
|
||||
<@includeModel object=returnTypeToConstruct/> ${resultName} = <#if factoryMethod??><@includeModel object=factoryMethod targetType=returnTypeToConstruct/><#else>new <@includeModel object=returnTypeToConstruct/>()</#if>;
|
||||
</#if>
|
||||
|
||||
</#if>
|
||||
<#list beforeMappingReferencesWithMappingTarget as callback>
|
||||
|
@ -16,6 +16,8 @@
|
||||
<#-- method is referenced java8 static method in the mapper to implement (interface) -->
|
||||
<#elseif static>
|
||||
<@includeModel object=definingType/>.<@methodCall/>
|
||||
<#elseif constructor>
|
||||
new <@includeModel object=definingType/><#if (parameterBindings?size > 0)>( <@arguments/> )<#else>()</#if>
|
||||
<#else>
|
||||
<@methodCall/>
|
||||
</#if>
|
||||
|
@ -11,7 +11,7 @@
|
||||
<@lib.sourceLocalVarAssignment/>
|
||||
<@lib.handleSourceReferenceNullCheck>
|
||||
for ( <@includeModel object=adderType.typeBound/> ${sourceLoopVarName} : <#if sourceLocalVarName??>${sourceLocalVarName}<#else>${sourceReference}</#if> ) {
|
||||
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/></@lib.handleWrite>;
|
||||
<#if ext.targetBeanName?has_content>${ext.targetBeanName}.</#if>${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/></@lib.handleWrite>;
|
||||
}
|
||||
</@lib.handleSourceReferenceNullCheck>
|
||||
</@lib.handleExceptions>
|
@ -10,6 +10,6 @@
|
||||
<@lib.handleExceptions>
|
||||
<@lib.sourceLocalVarAssignment/>
|
||||
<@lib.handleSourceReferenceNullCheck>
|
||||
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite>Arrays.copyOf( ${sourceLocalVarName}, ${sourceLocalVarName}.length )</@lib.handleWrite>;
|
||||
<#if ext.targetBeanName?has_content>${ext.targetBeanName}.</#if>${ext.targetWriteAccessorName}<@lib.handleWrite>Arrays.copyOf( ${sourceLocalVarName}, ${sourceLocalVarName}.length )</@lib.handleWrite>;
|
||||
</@lib.handleSourceReferenceNullCheck>
|
||||
</@lib.handleExceptions>
|
@ -10,6 +10,6 @@
|
||||
<@lib.handleExceptions>
|
||||
<@lib.sourceLocalVarAssignment/>
|
||||
<@lib.handleSourceReferenceNullCheck>
|
||||
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/></@lib.handleWrite>;
|
||||
<#if ext.targetBeanName?has_content>${ext.targetBeanName}.</#if>${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/></@lib.handleWrite>;
|
||||
</@lib.handleSourceReferenceNullCheck>
|
||||
</@lib.handleExceptions>
|
@ -9,5 +9,5 @@
|
||||
<#import "../macro/CommonMacros.ftl" as lib>
|
||||
<@lib.sourceLocalVarAssignment/>
|
||||
<@lib.handleExceptions>
|
||||
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/></@lib.handleWrite>;
|
||||
<#if ext.targetBeanName?has_content>${ext.targetBeanName}.</#if>${ext.targetWriteAccessorName}<@lib.handleWrite><@lib.handleAssignment/></@lib.handleWrite>;
|
||||
</@lib.handleExceptions>
|
@ -16,8 +16,12 @@
|
||||
-->
|
||||
<#macro callTargetWriteAccessor>
|
||||
<@lib.handleLocalVarNullCheck needs_explicit_local_var=directAssignment>
|
||||
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if directAssignment><@wrapLocalVarInCollectionInitializer/><#else><@lib.handleWithAssignmentOrNullCheckVar/></#if></@lib.handleWrite>;
|
||||
<#if ext.targetBeanName?has_content>${ext.targetBeanName}.</#if>${ext.targetWriteAccessorName}<@lib.handleWrite><#if directAssignment><@wrapLocalVarInCollectionInitializer/><#else><@lib.handleWithAssignmentOrNullCheckVar/></#if></@lib.handleWrite>;
|
||||
</@lib.handleLocalVarNullCheck>
|
||||
<#if !ext.defaultValueAssignment?? && !sourcePresenceCheckerReference?? && !ext.targetBeanName?has_content>else {<#-- the opposite (defaultValueAssignment) case is handeld inside lib.handleLocalVarNullCheck -->
|
||||
${ext.targetWriteAccessorName}<@lib.handleWrite>null</@lib.handleWrite>;
|
||||
}
|
||||
</#if>
|
||||
</#macro>
|
||||
<#--
|
||||
wraps the local variable in a collection initializer (new collection, or EnumSet.copyOf)
|
||||
|
@ -40,7 +40,7 @@
|
||||
}
|
||||
<#elseif setExplicitlyToDefault || setExplicitlyToNull>
|
||||
else {
|
||||
${ext.targetBeanName}.${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else>null</#if></@lib.handleWrite>;
|
||||
<#if ext.targetBeanName?has_content>${ext.targetBeanName}.</#if>${ext.targetWriteAccessorName}<@lib.handleWrite><#if setExplicitlyToDefault><@lib.initTargetObject/><#else>null</#if></@lib.handleWrite>;
|
||||
}
|
||||
</#if>
|
||||
</#macro>
|
||||
|
@ -65,12 +65,7 @@ public class Issue1242Test {
|
||||
".lang.Class<org.mapstruct.ap.test.bugs._1242.TargetB> clazz), org.mapstruct.ap.test.bugs._1242" +
|
||||
".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB(@TargetType java.lang" +
|
||||
".Class<org.mapstruct.ap.test.bugs._1242.TargetB> clazz), org.mapstruct.ap.test.bugs._1242" +
|
||||
".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB()."),
|
||||
@Diagnostic(type = ErroneousIssue1242MapperMultipleSources.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 20,
|
||||
message = "org.mapstruct.ap.test.bugs._1242.TargetB does not have an accessible parameterless " +
|
||||
"constructor.")
|
||||
".TargetB org.mapstruct.ap.test.bugs._1242.TargetFactories.createTargetB().")
|
||||
})
|
||||
public void ambiguousMethodErrorForTwoFactoryMethodsWithSourceParam() {
|
||||
}
|
||||
|
@ -33,8 +33,7 @@ public class Issue1283Test {
|
||||
@Diagnostic(type = ErroneousInverseTargetHasNoSuitableConstructorMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 22L,
|
||||
message = "org.mapstruct.ap.test.bugs._1283.Source does not have an accessible parameterless " +
|
||||
"constructor."
|
||||
message = "org.mapstruct.ap.test.bugs._1283.Source does not have an accessible constructor."
|
||||
)
|
||||
}
|
||||
)
|
||||
@ -49,8 +48,7 @@ public class Issue1283Test {
|
||||
@Diagnostic(type = ErroneousTargetHasNoSuitableConstructorMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 18L,
|
||||
message = "org.mapstruct.ap.test.bugs._1283.Source does not have an accessible parameterless " +
|
||||
"constructor."
|
||||
message = "org.mapstruct.ap.test.bugs._1283.Source does not have an accessible constructor."
|
||||
)
|
||||
}
|
||||
)
|
||||
|
@ -12,7 +12,7 @@ public class Source {
|
||||
|
||||
private String source;
|
||||
|
||||
public Source(String source) {
|
||||
private Source(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.CONSTRUCTOR)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ConstructorProperties {
|
||||
|
||||
String[] value();
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Documented
|
||||
@Target(ElementType.CONSTRUCTOR)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Default {
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class Person {
|
||||
|
||||
private final String name;
|
||||
private final int age;
|
||||
private final String job;
|
||||
private final String city;
|
||||
private final String address;
|
||||
private final List<String> children;
|
||||
|
||||
public Person(String name, int age, String job, String city, String address,
|
||||
List<String> children) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
this.job = job;
|
||||
this.city = city;
|
||||
this.address = address;
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public String getJob() {
|
||||
return job;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public List<String> getChildren() {
|
||||
return children;
|
||||
}
|
||||
}
|
@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class PersonDto {
|
||||
|
||||
private String name;
|
||||
private int age;
|
||||
private String job;
|
||||
private String city;
|
||||
private String address;
|
||||
private List<String> children;
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(int age) {
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public String getJob() {
|
||||
return job;
|
||||
}
|
||||
|
||||
public void setJob(String job) {
|
||||
this.job = job;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public void setCity(String city) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public List<String> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List<String> children) {
|
||||
this.children = children;
|
||||
}
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.constructorproperties;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.mapstruct.ap.test.constructor.ConstructorProperties;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class PersonWithConstructorProperties {
|
||||
|
||||
private final String name;
|
||||
private final int age;
|
||||
private final String job;
|
||||
private final String city;
|
||||
private final String address;
|
||||
private final List<String> children;
|
||||
|
||||
@ConstructorProperties({"name", "age", "job", "city", "address", "children"})
|
||||
public PersonWithConstructorProperties(String var1, int var2, String var3, String var4, String var5,
|
||||
List<String> var6) {
|
||||
this.name = var1;
|
||||
this.age = var2;
|
||||
this.job = var3;
|
||||
this.city = var4;
|
||||
this.address = var5;
|
||||
this.children = var6;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public String getJob() {
|
||||
return job;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public List<String> getChildren() {
|
||||
return children;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.constructorproperties;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.ap.test.constructor.PersonDto;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper
|
||||
public interface SimpleConstructorPropertiesMapper {
|
||||
|
||||
SimpleConstructorPropertiesMapper INSTANCE = Mappers.getMapper( SimpleConstructorPropertiesMapper.class );
|
||||
|
||||
PersonWithConstructorProperties map(PersonDto dto);
|
||||
|
||||
@Mapping(target = "age", constant = "25")
|
||||
@Mapping(target = "job", constant = "Software Developer")
|
||||
PersonWithConstructorProperties mapWithConstants(PersonDto dto);
|
||||
|
||||
@Mapping(target = "age", expression = "java(25 - 5)")
|
||||
@Mapping(target = "job", expression = "java(\"Software Developer\".toLowerCase())")
|
||||
PersonWithConstructorProperties mapWithExpression(PersonDto dto);
|
||||
}
|
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.constructorproperties;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.test.constructor.ConstructorProperties;
|
||||
import org.mapstruct.ap.test.constructor.PersonDto;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@WithClasses({
|
||||
ConstructorProperties.class,
|
||||
PersonWithConstructorProperties.class,
|
||||
PersonDto.class,
|
||||
SimpleConstructorPropertiesMapper.class
|
||||
})
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class SimpleConstructorPropertiesTest {
|
||||
|
||||
@Test
|
||||
public void mapDefault() {
|
||||
PersonDto source = new PersonDto();
|
||||
source.setName( "Bob" );
|
||||
source.setAge( 30 );
|
||||
source.setJob( "Software Engineer" );
|
||||
source.setCity( "Zurich" );
|
||||
source.setAddress( "Plaza 1" );
|
||||
source.setChildren( Arrays.asList( "Alice", "Tom" ) );
|
||||
|
||||
PersonWithConstructorProperties target = SimpleConstructorPropertiesMapper.INSTANCE.map( source );
|
||||
|
||||
assertThat( target.getName() ).isEqualTo( "Bob" );
|
||||
assertThat( target.getAge() ).isEqualTo( 30 );
|
||||
assertThat( target.getJob() ).isEqualTo( "Software Engineer" );
|
||||
assertThat( target.getCity() ).isEqualTo( "Zurich" );
|
||||
assertThat( target.getAddress() ).isEqualTo( "Plaza 1" );
|
||||
assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapWithConstants() {
|
||||
PersonDto source = new PersonDto();
|
||||
source.setName( "Bob" );
|
||||
source.setAge( 30 );
|
||||
source.setJob( "Software Engineer" );
|
||||
source.setCity( "Zurich" );
|
||||
source.setAddress( "Plaza 1" );
|
||||
source.setChildren( Arrays.asList( "Alice", "Tom" ) );
|
||||
|
||||
PersonWithConstructorProperties target = SimpleConstructorPropertiesMapper.INSTANCE.mapWithConstants( source );
|
||||
|
||||
assertThat( target.getName() ).isEqualTo( "Bob" );
|
||||
assertThat( target.getAge() ).isEqualTo( 25 );
|
||||
assertThat( target.getJob() ).isEqualTo( "Software Developer" );
|
||||
assertThat( target.getCity() ).isEqualTo( "Zurich" );
|
||||
assertThat( target.getAddress() ).isEqualTo( "Plaza 1" );
|
||||
assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapWithExpressions() {
|
||||
PersonDto source = new PersonDto();
|
||||
source.setName( "Bob" );
|
||||
source.setAge( 30 );
|
||||
source.setJob( "Software Engineer" );
|
||||
source.setCity( "Zurich" );
|
||||
source.setAddress( "Plaza 1" );
|
||||
source.setChildren( Arrays.asList( "Alice", "Tom" ) );
|
||||
|
||||
PersonWithConstructorProperties target = SimpleConstructorPropertiesMapper.INSTANCE.mapWithExpression( source );
|
||||
|
||||
assertThat( target.getName() ).isEqualTo( "Bob" );
|
||||
assertThat( target.getAge() ).isEqualTo( 20 );
|
||||
assertThat( target.getJob() ).isEqualTo( "software developer" );
|
||||
assertThat( target.getCity() ).isEqualTo( "Zurich" );
|
||||
assertThat( target.getAddress() ).isEqualTo( "Plaza 1" );
|
||||
assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" );
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.defaultannotated;
|
||||
|
||||
import org.mapstruct.ap.test.constructor.Default;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class PersonWithDefaultAnnotatedConstructor {
|
||||
|
||||
private final String name;
|
||||
private final int age;
|
||||
|
||||
public PersonWithDefaultAnnotatedConstructor(String name) {
|
||||
this( name, -1 );
|
||||
}
|
||||
|
||||
@Default
|
||||
public PersonWithDefaultAnnotatedConstructor(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.defaultannotated;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.ap.test.constructor.PersonDto;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper
|
||||
public interface SimpleDefaultAnnotatedConstructorMapper {
|
||||
|
||||
SimpleDefaultAnnotatedConstructorMapper INSTANCE =
|
||||
Mappers.getMapper( SimpleDefaultAnnotatedConstructorMapper.class );
|
||||
|
||||
PersonWithDefaultAnnotatedConstructor map(PersonDto dto);
|
||||
|
||||
@Mapping(target = "age", constant = "25")
|
||||
PersonWithDefaultAnnotatedConstructor mapWithConstants(PersonDto dto);
|
||||
|
||||
@Mapping(target = "age", expression = "java(25 - 5)")
|
||||
PersonWithDefaultAnnotatedConstructor mapWithExpression(PersonDto dto);
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.defaultannotated;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.test.constructor.Default;
|
||||
import org.mapstruct.ap.test.constructor.PersonDto;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@WithClasses({
|
||||
Default.class,
|
||||
PersonWithDefaultAnnotatedConstructor.class,
|
||||
PersonDto.class,
|
||||
SimpleDefaultAnnotatedConstructorMapper.class
|
||||
})
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class SimpleDefaultAnnotatedConstructorTest {
|
||||
|
||||
@Test
|
||||
public void mapDefault() {
|
||||
PersonDto source = new PersonDto();
|
||||
source.setName( "Bob" );
|
||||
source.setAge( 30 );
|
||||
source.setJob( "Software Engineer" );
|
||||
source.setCity( "Zurich" );
|
||||
source.setAddress( "Plaza 1" );
|
||||
source.setChildren( Arrays.asList( "Alice", "Tom" ) );
|
||||
|
||||
PersonWithDefaultAnnotatedConstructor target = SimpleDefaultAnnotatedConstructorMapper.INSTANCE.map( source );
|
||||
|
||||
assertThat( target.getName() ).isEqualTo( "Bob" );
|
||||
assertThat( target.getAge() ).isEqualTo( 30 );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapWithConstants() {
|
||||
PersonDto source = new PersonDto();
|
||||
source.setName( "Bob" );
|
||||
source.setAge( 30 );
|
||||
source.setJob( "Software Engineer" );
|
||||
source.setCity( "Zurich" );
|
||||
source.setAddress( "Plaza 1" );
|
||||
source.setChildren( Arrays.asList( "Alice", "Tom" ) );
|
||||
|
||||
PersonWithDefaultAnnotatedConstructor target =
|
||||
SimpleDefaultAnnotatedConstructorMapper.INSTANCE.mapWithConstants( source );
|
||||
|
||||
assertThat( target.getName() ).isEqualTo( "Bob" );
|
||||
assertThat( target.getAge() ).isEqualTo( 25 );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapWithExpressions() {
|
||||
PersonDto source = new PersonDto();
|
||||
source.setName( "Bob" );
|
||||
source.setAge( 30 );
|
||||
source.setJob( "Software Engineer" );
|
||||
source.setCity( "Zurich" );
|
||||
source.setAddress( "Plaza 1" );
|
||||
source.setChildren( Arrays.asList( "Alice", "Tom" ) );
|
||||
|
||||
PersonWithDefaultAnnotatedConstructor target =
|
||||
SimpleDefaultAnnotatedConstructorMapper.INSTANCE.mapWithExpression( source );
|
||||
|
||||
assertThat( target.getName() ).isEqualTo( "Bob" );
|
||||
assertThat( target.getAge() ).isEqualTo( 20 );
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.erroneous;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ap.test.constructor.PersonDto;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper
|
||||
public interface ErroneousAmbiguousConstructorsMapper {
|
||||
|
||||
PersonWithMultipleConstructors map(PersonDto dto);
|
||||
|
||||
class PersonWithMultipleConstructors {
|
||||
|
||||
private final String name;
|
||||
private final int age;
|
||||
|
||||
public PersonWithMultipleConstructors(String name) {
|
||||
this( name, -1 );
|
||||
}
|
||||
|
||||
public PersonWithMultipleConstructors(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.erroneous;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ap.test.constructor.PersonDto;
|
||||
import org.mapstruct.ap.test.constructor.ConstructorProperties;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper
|
||||
public interface ErroneousConstructorPropertiesMapper {
|
||||
|
||||
PersonWithIncorrectConstructorProperties map(PersonDto dto);
|
||||
|
||||
class PersonWithIncorrectConstructorProperties {
|
||||
|
||||
private final String name;
|
||||
private final int age;
|
||||
|
||||
@ConstructorProperties({ "name" })
|
||||
public PersonWithIncorrectConstructorProperties(String name, int age) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.erroneous;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.test.constructor.ConstructorProperties;
|
||||
import org.mapstruct.ap.test.constructor.PersonDto;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class ErroneousConstructorTest {
|
||||
|
||||
@Test
|
||||
@WithClasses({
|
||||
ErroneousAmbiguousConstructorsMapper.class,
|
||||
PersonDto.class
|
||||
})
|
||||
@ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = {
|
||||
@Diagnostic(
|
||||
type = ErroneousAmbiguousConstructorsMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 17,
|
||||
message = "Ambiguous constructors found for creating org.mapstruct.ap.test.constructor.erroneous" +
|
||||
".ErroneousAmbiguousConstructorsMapper.PersonWithMultipleConstructors. Either declare parameterless " +
|
||||
"constructor or annotate the default constructor with an annotation named @Default."
|
||||
)
|
||||
})
|
||||
public void shouldUseMultipleConstructors() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({
|
||||
ConstructorProperties.class,
|
||||
ErroneousConstructorPropertiesMapper.class,
|
||||
PersonDto.class
|
||||
})
|
||||
@ExpectedCompilationOutcome(value = CompilationResult.FAILED, diagnostics = {
|
||||
@Diagnostic(
|
||||
type = ErroneousConstructorPropertiesMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 18,
|
||||
message = "Incorrect @ConstructorProperties for org.mapstruct.ap.test.constructor.erroneous" +
|
||||
".ErroneousConstructorPropertiesMapper.PersonWithIncorrectConstructorProperties. The size of the " +
|
||||
"@ConstructorProperties does not match the number of constructor parameters")
|
||||
})
|
||||
public void shouldNotCompileIfConstructorPropertiesDoesNotMatch() {
|
||||
}
|
||||
}
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.mixed;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ap.test.constructor.PersonDto;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper
|
||||
public interface ConstructorMixedWithSettersMapper {
|
||||
|
||||
ConstructorMixedWithSettersMapper INSTANCE = Mappers.getMapper( ConstructorMixedWithSettersMapper.class );
|
||||
|
||||
PersonMixed map(PersonDto dto);
|
||||
}
|
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.mixed;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.test.constructor.PersonDto;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@WithClasses({
|
||||
PersonMixed.class,
|
||||
PersonDto.class,
|
||||
ConstructorMixedWithSettersMapper.class
|
||||
})
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class ConstructorMixedWithSettersTest {
|
||||
|
||||
@Test
|
||||
public void mapDefault() {
|
||||
PersonDto source = new PersonDto();
|
||||
source.setName( "Bob" );
|
||||
source.setAge( 30 );
|
||||
source.setJob( "Software Engineer" );
|
||||
source.setCity( "Zurich" );
|
||||
source.setAddress( "Plaza 1" );
|
||||
source.setChildren( Arrays.asList( "Alice", "Tom" ) );
|
||||
|
||||
PersonMixed target = ConstructorMixedWithSettersMapper.INSTANCE.map( source );
|
||||
|
||||
assertThat( target.getName() ).isEqualTo( "Bob" );
|
||||
assertThat( target.getAge() ).isEqualTo( 30 );
|
||||
assertThat( target.getJob() ).isEqualTo( "Software Engineer" );
|
||||
assertThat( target.getCity() ).isEqualTo( "Zurich" );
|
||||
assertThat( target.getAddress() ).isEqualTo( "Plaza 1" );
|
||||
assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" );
|
||||
}
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.mixed;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class PersonMixed {
|
||||
|
||||
private final String name;
|
||||
private final int age;
|
||||
private final String job;
|
||||
private String city;
|
||||
private String address;
|
||||
private List<String> children;
|
||||
|
||||
public PersonMixed(String name, int age, String job) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
this.job = job;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
throw new RuntimeException( "Method is here only to verify that MapStruct won't use it" );
|
||||
}
|
||||
|
||||
public int getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public String getJob() {
|
||||
return job;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public void setCity(String city) {
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public String getAddress() {
|
||||
return address;
|
||||
}
|
||||
|
||||
public void setAddress(String address) {
|
||||
this.address = address;
|
||||
}
|
||||
|
||||
public List<String> getChildren() {
|
||||
return children;
|
||||
}
|
||||
|
||||
public void setChildren(List<String> children) {
|
||||
this.children = children;
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Chart;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Song;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public interface ArtistToChartEntry {
|
||||
|
||||
ArtistToChartEntry MAPPER = Mappers.getMapper( ArtistToChartEntry.class );
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "chartName", source = "chart.name"),
|
||||
@Mapping(target = "songTitle", source = "song.title"),
|
||||
@Mapping(target = "artistName", source = "song.artist.name"),
|
||||
@Mapping(target = "recordedAt", source = "song.artist.label.studio.name"),
|
||||
@Mapping(target = "city", source = "song.artist.label.studio.city"),
|
||||
@Mapping(target = "position", source = "position")
|
||||
})
|
||||
ChartEntry map(Chart chart, Song song, Integer position);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "chartName", ignore = true),
|
||||
@Mapping(target = "songTitle", source = "title"),
|
||||
@Mapping(target = "artistName", source = "artist.name"),
|
||||
@Mapping(target = "recordedAt", source = "artist.label.studio.name"),
|
||||
@Mapping(target = "city", source = "artist.label.studio.city"),
|
||||
@Mapping(target = "position", ignore = true)
|
||||
})
|
||||
ChartEntry map(Song song);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "chartName", source = "name"),
|
||||
@Mapping(target = "songTitle", ignore = true),
|
||||
@Mapping(target = "artistName", ignore = true),
|
||||
@Mapping(target = "recordedAt", ignore = true),
|
||||
@Mapping(target = "city", ignore = true),
|
||||
@Mapping(target = "position", ignore = true)
|
||||
})
|
||||
ChartEntry map(Chart name);
|
||||
|
||||
}
|
@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource;
|
||||
|
||||
import org.mapstruct.InheritInverseConfiguration;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryComposed;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryLabel;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Label;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Song;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public abstract class ArtistToChartEntryComposedReverse {
|
||||
|
||||
public static final ArtistToChartEntryComposedReverse MAPPER =
|
||||
Mappers.getMapper( ArtistToChartEntryComposedReverse.class );
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "songTitle", source = "title"),
|
||||
@Mapping(target = "artistName", source = "artist.name"),
|
||||
@Mapping(target = "label", source = "artist.label"),
|
||||
@Mapping(target = "position", ignore = true),
|
||||
@Mapping(target = "chartName", ignore = true )
|
||||
})
|
||||
abstract ChartEntryComposed mapForward(Song song);
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "name", source = "name"),
|
||||
@Mapping(target = "recordedAt", source = "studio.name"),
|
||||
@Mapping(target = "city", source = "studio.city")
|
||||
})
|
||||
abstract ChartEntryLabel mapForward(Label label);
|
||||
|
||||
@InheritInverseConfiguration
|
||||
@Mapping(target = "positions", ignore = true)
|
||||
abstract Song mapReverse(ChartEntryComposed ce);
|
||||
|
||||
@InheritInverseConfiguration
|
||||
abstract Label mapReverse(ChartEntryLabel label);
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource;
|
||||
|
||||
import org.mapstruct.MapperConfig;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.BaseChartEntry;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Song;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@MapperConfig( unmappedTargetPolicy = ReportingPolicy.ERROR )
|
||||
public interface ArtistToChartEntryConfig {
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "songTitle", source = "title"),
|
||||
@Mapping(target = "artistName", source = "artist.name"),
|
||||
@Mapping(target = "chartName", ignore = true )
|
||||
})
|
||||
BaseChartEntry mapForwardConfig( Song song );
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource;
|
||||
|
||||
import org.mapstruct.InheritInverseConfiguration;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Song;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public abstract class ArtistToChartEntryReverse {
|
||||
|
||||
public static final ArtistToChartEntryReverse MAPPER = Mappers.getMapper( ArtistToChartEntryReverse.class );
|
||||
|
||||
@Mappings({
|
||||
|
||||
@Mapping(target = "songTitle", source = "title"),
|
||||
@Mapping(target = "artistName", source = "artist.name"),
|
||||
@Mapping(target = "recordedAt", source = "artist.label.studio.name"),
|
||||
@Mapping(target = "city", source = "artist.label.studio.city"),
|
||||
@Mapping(target = "position", ignore = true),
|
||||
@Mapping(target = "chartName", ignore = true )
|
||||
})
|
||||
abstract ChartEntry mapForward(Song song);
|
||||
|
||||
@InheritInverseConfiguration
|
||||
@Mapping(target = "positions", ignore = true)
|
||||
abstract Song mapReverse(ChartEntry ce);
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource;
|
||||
|
||||
import org.mapstruct.InheritConfiguration;
|
||||
import org.mapstruct.InheritInverseConfiguration;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryWithBase;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Song;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper( config = ArtistToChartEntryConfig.class )
|
||||
public abstract class ArtistToChartEntryWithConfigReverse {
|
||||
|
||||
public static final ArtistToChartEntryWithConfigReverse MAPPER =
|
||||
Mappers.getMapper( ArtistToChartEntryWithConfigReverse.class );
|
||||
|
||||
@InheritConfiguration
|
||||
@Mappings({
|
||||
@Mapping(target = "recordedAt", source = "artist.label.studio.name"),
|
||||
@Mapping(target = "city", source = "artist.label.studio.city"),
|
||||
@Mapping(target = "position", ignore = true)
|
||||
})
|
||||
abstract ChartEntryWithBase mapForward(Song song);
|
||||
|
||||
@InheritInverseConfiguration( name = "mapForward" )
|
||||
@Mapping(target = "positions", ignore = true)
|
||||
abstract Song mapReverse(ChartEntryWithBase ce);
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource;
|
||||
|
||||
import org.mapstruct.InheritInverseConfiguration;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Song;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public abstract class ArtistToChartEntryWithFactoryReverse {
|
||||
|
||||
public static final ArtistToChartEntryWithFactoryReverse MAPPER
|
||||
= Mappers.getMapper( ArtistToChartEntryWithFactoryReverse.class );
|
||||
|
||||
@Mappings({
|
||||
|
||||
@Mapping(target = "songTitle", source = "title"),
|
||||
@Mapping(target = "artistName", source = "artist.name"),
|
||||
@Mapping(target = "recordedAt", source = "artist.label.studio.name"),
|
||||
@Mapping(target = "city", source = "artist.label.studio.city"),
|
||||
@Mapping(target = "position", ignore = true),
|
||||
@Mapping(target = "chartName", ignore = true)
|
||||
})
|
||||
abstract ChartEntry mapForward(Song song);
|
||||
|
||||
@InheritInverseConfiguration
|
||||
@Mapping(target = "positions", ignore = true)
|
||||
abstract Song mapReverse(ChartEntry ce);
|
||||
}
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource;
|
||||
|
||||
import org.mapstruct.InheritInverseConfiguration;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Song;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public abstract class ArtistToChartEntryWithIgnoresReverse {
|
||||
|
||||
public static final ArtistToChartEntryWithIgnoresReverse MAPPER =
|
||||
Mappers.getMapper( ArtistToChartEntryWithIgnoresReverse.class );
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "songTitle", source = "title"),
|
||||
@Mapping(target = "artistName", source = "artist.name"),
|
||||
@Mapping(target = "recordedAt", source = "artist.label.studio.name"),
|
||||
@Mapping(target = "city", source = "artist.label.studio.city"),
|
||||
@Mapping(target = "position", ignore = true),
|
||||
@Mapping(target = "chartName", ignore = true)
|
||||
})
|
||||
abstract ChartEntry mapForward(Song song);
|
||||
|
||||
@InheritInverseConfiguration
|
||||
@Mappings({
|
||||
@Mapping(target = "positions", ignore = true),
|
||||
@Mapping(target = "artist", ignore = true)
|
||||
})
|
||||
abstract Song mapReverse(ChartEntry ce);
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource;
|
||||
|
||||
import org.mapstruct.InheritInverseConfiguration;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryWithMapping;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Song;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public abstract class ArtistToChartEntryWithMappingReverse {
|
||||
|
||||
public static final ArtistToChartEntryWithMappingReverse MAPPER =
|
||||
Mappers.getMapper( ArtistToChartEntryWithMappingReverse.class );
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "songTitle", source = "title"),
|
||||
@Mapping(target = "artistId", source = "artist.name"),
|
||||
@Mapping(target = "recordedAt", source = "artist.label.studio.name"),
|
||||
@Mapping(target = "city", source = "artist.label.studio.city"),
|
||||
@Mapping(target = "position", ignore = true),
|
||||
@Mapping(target = "chartName", ignore = true)
|
||||
})
|
||||
abstract ChartEntryWithMapping mapForward(Song song);
|
||||
|
||||
@InheritInverseConfiguration
|
||||
@Mapping(target = "positions", ignore = true)
|
||||
abstract Song mapReverse(ChartEntryWithMapping ce);
|
||||
|
||||
int mapArtistToArtistId(String in) {
|
||||
|
||||
if ( "The Beatles".equals( in ) ) {
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
String mapArtistIdToArtist(int in) {
|
||||
|
||||
if ( in == 1 ) {
|
||||
return "The Beatles";
|
||||
}
|
||||
else {
|
||||
return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,132 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Artist;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Chart;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Label;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Song;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Studio;
|
||||
import org.mapstruct.ap.testutil.IssueKey;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
import org.mapstruct.ap.testutil.runner.GeneratedSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@WithClasses({ Song.class, Artist.class, Chart.class, Label.class, Studio.class, ChartEntry.class })
|
||||
@IssueKey("73")
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class NestedSourcePropertiesConstructorTest {
|
||||
|
||||
@Rule
|
||||
public final GeneratedSource generatedSource = new GeneratedSource();
|
||||
|
||||
@Test
|
||||
@WithClasses({ ArtistToChartEntry.class })
|
||||
public void shouldGenerateImplementationForPropertyNamesOnly() {
|
||||
generatedSource.addComparisonToFixtureFor( ArtistToChartEntry.class );
|
||||
|
||||
Studio studio = new Studio( "Abbey Road", "London" );
|
||||
|
||||
Label label = new Label( "EMY", studio );
|
||||
|
||||
Artist artist = new Artist( "The Beatles", label );
|
||||
|
||||
Song song = new Song( artist, "A Hard Day's Night", Collections.emptyList() );
|
||||
|
||||
ChartEntry chartEntry = ArtistToChartEntry.MAPPER.map( song );
|
||||
|
||||
assertThat( chartEntry ).isNotNull();
|
||||
assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" );
|
||||
assertThat( chartEntry.getChartName() ).isNull();
|
||||
assertThat( chartEntry.getCity() ).isEqualTo( "London" );
|
||||
assertThat( chartEntry.getPosition() ).isEqualTo( 0 );
|
||||
assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" );
|
||||
assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({ ArtistToChartEntry.class })
|
||||
public void shouldGenerateImplementationForMultipleParam() {
|
||||
|
||||
Studio studio = new Studio( "Abbey Road", "London" );
|
||||
|
||||
Label label = new Label( "EMY", studio );
|
||||
|
||||
Artist artist = new Artist( "The Beatles", label );
|
||||
|
||||
Song song = new Song( artist, "A Hard Day's Night", Collections.emptyList() );
|
||||
|
||||
Chart chart = new Chart( "record-sales", "Billboard", null );
|
||||
|
||||
ChartEntry chartEntry = ArtistToChartEntry.MAPPER.map( chart, song, 1 );
|
||||
|
||||
assertThat( chartEntry ).isNotNull();
|
||||
assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" );
|
||||
assertThat( chartEntry.getChartName() ).isEqualTo( "Billboard" );
|
||||
assertThat( chartEntry.getCity() ).isEqualTo( "London" );
|
||||
assertThat( chartEntry.getPosition() ).isEqualTo( 1 );
|
||||
assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" );
|
||||
assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" );
|
||||
|
||||
chartEntry = ArtistToChartEntry.MAPPER.map( null, song, 10 );
|
||||
|
||||
assertThat( chartEntry ).isNotNull();
|
||||
assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" );
|
||||
assertThat( chartEntry.getChartName() ).isNull();
|
||||
assertThat( chartEntry.getCity() ).isEqualTo( "London" );
|
||||
assertThat( chartEntry.getPosition() ).isEqualTo( 10 );
|
||||
assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" );
|
||||
assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" );
|
||||
|
||||
chartEntry = ArtistToChartEntry.MAPPER.map( chart, null, 5 );
|
||||
|
||||
assertThat( chartEntry ).isNotNull();
|
||||
assertThat( chartEntry.getArtistName() ).isNull();
|
||||
assertThat( chartEntry.getChartName() ).isEqualTo( "Billboard" );
|
||||
assertThat( chartEntry.getCity() ).isNull();
|
||||
assertThat( chartEntry.getPosition() ).isEqualTo( 5 );
|
||||
assertThat( chartEntry.getRecordedAt() ).isNull();
|
||||
assertThat( chartEntry.getSongTitle() ).isNull();
|
||||
|
||||
chartEntry = ArtistToChartEntry.MAPPER.map( chart, song, null );
|
||||
|
||||
assertThat( chartEntry ).isNotNull();
|
||||
assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" );
|
||||
assertThat( chartEntry.getChartName() ).isEqualTo( "Billboard" );
|
||||
assertThat( chartEntry.getCity() ).isEqualTo( "London" );
|
||||
assertThat( chartEntry.getPosition() ).isEqualTo( 0 );
|
||||
assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" );
|
||||
assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({ ArtistToChartEntry.class })
|
||||
public void shouldPickPropertyNameOverParameterName() {
|
||||
|
||||
Chart chart = new Chart( "record-sales", "Billboard", null );
|
||||
|
||||
ChartEntry chartEntry = ArtistToChartEntry.MAPPER.map( chart );
|
||||
|
||||
assertThat( chartEntry ).isNotNull();
|
||||
assertThat( chartEntry.getArtistName() ).isNull();
|
||||
assertThat( chartEntry.getChartName() ).isEqualTo( "Billboard" );
|
||||
assertThat( chartEntry.getCity() ).isNull();
|
||||
assertThat( chartEntry.getPosition() ).isEqualTo( 0 );
|
||||
assertThat( chartEntry.getRecordedAt() ).isNull();
|
||||
assertThat( chartEntry.getSongTitle() ).isNull();
|
||||
}
|
||||
}
|
@ -0,0 +1,222 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.BaseChartEntry;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryComposed;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryLabel;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryWithBase;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntryWithMapping;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Artist;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Chart;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Label;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Song;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Studio;
|
||||
import org.mapstruct.ap.testutil.IssueKey;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@IssueKey("73")
|
||||
@WithClasses({ Song.class, Artist.class, Chart.class, Label.class, Studio.class, ChartEntry.class })
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class ReversingNestedSourcePropertiesConstructorTest {
|
||||
|
||||
@Test
|
||||
@WithClasses({ ArtistToChartEntryReverse.class })
|
||||
public void shouldGenerateNestedReverse() {
|
||||
|
||||
Song song1 = prepareSong();
|
||||
|
||||
ChartEntry chartEntry = ArtistToChartEntryReverse.MAPPER.mapForward( song1 );
|
||||
|
||||
assertThat( chartEntry ).isNotNull();
|
||||
assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" );
|
||||
assertThat( chartEntry.getChartName() ).isNull();
|
||||
assertThat( chartEntry.getCity() ).isEqualTo( "London" );
|
||||
assertThat( chartEntry.getPosition() ).isEqualTo( 0 );
|
||||
assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" );
|
||||
assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" );
|
||||
|
||||
// and now in reverse
|
||||
Song song2 = ArtistToChartEntryReverse.MAPPER.mapReverse( chartEntry );
|
||||
|
||||
assertThat( song2 ).isNotNull();
|
||||
assertThat( song2.getArtist() ).isNotNull();
|
||||
assertThat( song2.getArtist().getName() ).isEqualTo( "The Beatles" );
|
||||
assertThat( song2.getArtist().getLabel() ).isNotNull();
|
||||
assertThat( song2.getArtist().getLabel().getName() ).isNull();
|
||||
assertThat( song2.getArtist().getLabel().getStudio() ).isNotNull();
|
||||
assertThat( song2.getArtist().getLabel().getStudio().getCity() ).isEqualTo( "London" );
|
||||
assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({ ArtistToChartEntryWithIgnoresReverse.class })
|
||||
public void shouldIgnoreEverytingBelowArtist() {
|
||||
|
||||
Song song1 = prepareSong();
|
||||
|
||||
ChartEntry chartEntry = ArtistToChartEntryWithIgnoresReverse.MAPPER.mapForward( song1 );
|
||||
|
||||
assertThat( chartEntry ).isNotNull();
|
||||
assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" );
|
||||
assertThat( chartEntry.getChartName() ).isNull();
|
||||
assertThat( chartEntry.getCity() ).isEqualTo( "London" );
|
||||
assertThat( chartEntry.getPosition() ).isEqualTo( 0 );
|
||||
assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" );
|
||||
assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" );
|
||||
|
||||
// and now in reverse
|
||||
Song song2 = ArtistToChartEntryWithIgnoresReverse.MAPPER.mapReverse( chartEntry );
|
||||
|
||||
assertThat( song2 ).isNotNull();
|
||||
assertThat( song2.getArtist() ).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({ ArtistToChartEntryWithFactoryReverse.class })
|
||||
public void shouldGenerateNestedReverseWithFactory() {
|
||||
|
||||
Song song1 = prepareSong();
|
||||
|
||||
ChartEntry chartEntry = ArtistToChartEntryWithFactoryReverse.MAPPER.mapForward( song1 );
|
||||
|
||||
assertThat( chartEntry ).isNotNull();
|
||||
assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" );
|
||||
assertThat( chartEntry.getChartName() ).isNull();
|
||||
assertThat( chartEntry.getCity() ).isEqualTo( "London" );
|
||||
assertThat( chartEntry.getPosition() ).isEqualTo( 0 );
|
||||
assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" );
|
||||
assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" );
|
||||
|
||||
// and now in reverse
|
||||
Song song2 = ArtistToChartEntryWithFactoryReverse.MAPPER.mapReverse( chartEntry );
|
||||
|
||||
assertThat( song2 ).isNotNull();
|
||||
assertThat( song2.getArtist() ).isNotNull();
|
||||
assertThat( song2.getArtist().getName() ).isEqualTo( "The Beatles" );
|
||||
assertThat( song2.getArtist().getLabel() ).isNotNull();
|
||||
assertThat( song2.getArtist().getLabel().getName() ).isNull();
|
||||
assertThat( song2.getArtist().getLabel().getStudio() ).isNotNull();
|
||||
assertThat( song2.getArtist().getLabel().getStudio().getCity() ).isEqualTo( "London" );
|
||||
assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" );
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({ ArtistToChartEntryComposedReverse.class, ChartEntryComposed.class, ChartEntryLabel.class })
|
||||
public void shouldGenerateNestedComposedReverse() {
|
||||
|
||||
Song song1 = prepareSong();
|
||||
|
||||
ChartEntryComposed chartEntry = ArtistToChartEntryComposedReverse.MAPPER.mapForward( song1 );
|
||||
|
||||
assertThat( chartEntry ).isNotNull();
|
||||
assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" );
|
||||
assertThat( chartEntry.getChartName() ).isNull();
|
||||
assertThat( chartEntry.getLabel().getName() ).isEqualTo( "EMY" );
|
||||
assertThat( chartEntry.getLabel().getCity() ).isEqualTo( "London" );
|
||||
assertThat( chartEntry.getLabel().getRecordedAt() ).isEqualTo( "Abbey Road" );
|
||||
assertThat( chartEntry.getPosition() ).isEqualTo( 0 );
|
||||
assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" );
|
||||
|
||||
// and now in reverse
|
||||
Song song2 = ArtistToChartEntryComposedReverse.MAPPER.mapReverse( chartEntry );
|
||||
|
||||
assertThat( song2 ).isNotNull();
|
||||
assertThat( song2.getArtist() ).isNotNull();
|
||||
assertThat( song2.getArtist().getName() ).isEqualTo( "The Beatles" );
|
||||
assertThat( song2.getArtist().getLabel() ).isNotNull();
|
||||
assertThat( song2.getArtist().getLabel().getName() ).isEqualTo( "EMY" );
|
||||
assertThat( song2.getArtist().getLabel().getStudio() ).isNotNull();
|
||||
assertThat( song2.getArtist().getLabel().getStudio().getCity() ).isEqualTo( "London" );
|
||||
assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({ ArtistToChartEntryWithMappingReverse.class, ChartEntryWithMapping.class })
|
||||
public void shouldGenerateNestedWithMappingReverse() {
|
||||
|
||||
Song song1 = prepareSong();
|
||||
|
||||
ChartEntryWithMapping chartEntry = ArtistToChartEntryWithMappingReverse.MAPPER.mapForward( song1 );
|
||||
|
||||
assertThat( chartEntry ).isNotNull();
|
||||
assertThat( chartEntry.getArtistId() ).isEqualTo( 1 );
|
||||
assertThat( chartEntry.getChartName() ).isNull();
|
||||
assertThat( chartEntry.getCity() ).isEqualTo( "London" );
|
||||
assertThat( chartEntry.getPosition() ).isEqualTo( 0 );
|
||||
assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" );
|
||||
assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" );
|
||||
|
||||
// and now in reverse
|
||||
Song song2 = ArtistToChartEntryWithMappingReverse.MAPPER.mapReverse( chartEntry );
|
||||
|
||||
assertThat( song2 ).isNotNull();
|
||||
assertThat( song2.getArtist() ).isNotNull();
|
||||
assertThat( song2.getArtist().getName() ).isEqualTo( "The Beatles" );
|
||||
assertThat( song2.getArtist().getLabel() ).isNotNull();
|
||||
assertThat( song2.getArtist().getLabel().getName() ).isNull();
|
||||
assertThat( song2.getArtist().getLabel().getStudio() ).isNotNull();
|
||||
assertThat( song2.getArtist().getLabel().getStudio().getCity() ).isEqualTo( "London" );
|
||||
assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses({
|
||||
ArtistToChartEntryWithConfigReverse.class,
|
||||
ArtistToChartEntryConfig.class,
|
||||
BaseChartEntry.class,
|
||||
ChartEntryWithBase.class
|
||||
})
|
||||
public void shouldGenerateNestedWithConfigReverse() {
|
||||
|
||||
Song song1 = prepareSong();
|
||||
|
||||
ChartEntryWithBase chartEntry = ArtistToChartEntryWithConfigReverse.MAPPER.mapForward( song1 );
|
||||
|
||||
assertThat( chartEntry ).isNotNull();
|
||||
assertThat( chartEntry.getArtistName() ).isEqualTo( "The Beatles" );
|
||||
assertThat( chartEntry.getChartName() ).isNull();
|
||||
assertThat( chartEntry.getCity() ).isEqualTo( "London" );
|
||||
assertThat( chartEntry.getPosition() ).isEqualTo( 0 );
|
||||
assertThat( chartEntry.getRecordedAt() ).isEqualTo( "Abbey Road" );
|
||||
assertThat( chartEntry.getSongTitle() ).isEqualTo( "A Hard Day's Night" );
|
||||
|
||||
// and now in reverse
|
||||
Song song2 = ArtistToChartEntryWithConfigReverse.MAPPER.mapReverse( chartEntry );
|
||||
|
||||
assertThat( song2 ).isNotNull();
|
||||
assertThat( song2.getArtist() ).isNotNull();
|
||||
assertThat( song2.getArtist().getName() ).isEqualTo( "The Beatles" );
|
||||
assertThat( song2.getArtist().getLabel() ).isNotNull();
|
||||
assertThat( song2.getArtist().getLabel().getName() ).isNull();
|
||||
assertThat( song2.getArtist().getLabel().getStudio() ).isNotNull();
|
||||
assertThat( song2.getArtist().getLabel().getStudio().getCity() ).isEqualTo( "London" );
|
||||
assertThat( song2.getArtist().getLabel().getStudio().getName() ).isEqualTo( "Abbey Road" );
|
||||
}
|
||||
|
||||
private Song prepareSong() {
|
||||
Studio studio = new Studio( "Abbey Road", "London" );
|
||||
|
||||
Label label = new Label( "EMY", studio );
|
||||
|
||||
Artist artist = new Artist( "The Beatles", label );
|
||||
|
||||
return new Song( artist, "A Hard Day's Night", Collections.emptyList() );
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource._target;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
public class AdderUsageObserver {
|
||||
|
||||
private AdderUsageObserver() {
|
||||
}
|
||||
|
||||
private static boolean used = false;
|
||||
|
||||
public static boolean isUsed() {
|
||||
return used;
|
||||
}
|
||||
|
||||
public static void setUsed(boolean used) {
|
||||
AdderUsageObserver.used = used;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource._target;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class BaseChartEntry {
|
||||
|
||||
private final String chartName;
|
||||
private final String songTitle;
|
||||
private final String artistName;
|
||||
|
||||
public BaseChartEntry(String chartName, String songTitle, String artistName) {
|
||||
this.chartName = chartName;
|
||||
this.songTitle = songTitle;
|
||||
this.artistName = artistName;
|
||||
}
|
||||
|
||||
public String getChartName() {
|
||||
return chartName;
|
||||
}
|
||||
|
||||
public String getSongTitle() {
|
||||
return songTitle;
|
||||
}
|
||||
|
||||
public String getArtistName() {
|
||||
return artistName;
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource._target;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class ChartEntry {
|
||||
|
||||
private final String chartName;
|
||||
private final String songTitle;
|
||||
private final String artistName;
|
||||
private final String recordedAt;
|
||||
private final String city;
|
||||
private final int position;
|
||||
|
||||
public ChartEntry(String chartName, String songTitle, String artistName, String recordedAt, String city,
|
||||
int position) {
|
||||
this.chartName = chartName;
|
||||
this.songTitle = songTitle;
|
||||
this.artistName = artistName;
|
||||
this.recordedAt = recordedAt;
|
||||
this.city = city;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public String getChartName() {
|
||||
return chartName;
|
||||
}
|
||||
|
||||
public String getSongTitle() {
|
||||
return songTitle;
|
||||
}
|
||||
|
||||
public String getArtistName() {
|
||||
return artistName;
|
||||
}
|
||||
|
||||
public String getRecordedAt() {
|
||||
return recordedAt;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource._target;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class ChartEntryComposed {
|
||||
|
||||
private String chartName;
|
||||
private String songTitle;
|
||||
private String artistName;
|
||||
private ChartEntryLabel label;
|
||||
private int position;
|
||||
|
||||
public String getChartName() {
|
||||
return chartName;
|
||||
}
|
||||
|
||||
public void setChartName(String chartName) {
|
||||
this.chartName = chartName;
|
||||
}
|
||||
|
||||
public String getSongTitle() {
|
||||
return songTitle;
|
||||
}
|
||||
|
||||
public void setSongTitle(String songTitle) {
|
||||
this.songTitle = songTitle;
|
||||
}
|
||||
|
||||
public String getArtistName() {
|
||||
return artistName;
|
||||
}
|
||||
|
||||
public void setArtistName(String artistName) {
|
||||
this.artistName = artistName;
|
||||
}
|
||||
|
||||
public ChartEntryLabel getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
public void setLabel(ChartEntryLabel label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public void setPosition(int position) {
|
||||
this.position = position;
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource._target;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class ChartEntryLabel {
|
||||
|
||||
private final String name;
|
||||
private final String city;
|
||||
private final String recordedAt;
|
||||
|
||||
public ChartEntryLabel(String name, String city, String recordedAt) {
|
||||
this.name = name;
|
||||
this.city = city;
|
||||
this.recordedAt = recordedAt;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public String getRecordedAt() {
|
||||
return recordedAt;
|
||||
}
|
||||
}
|
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource._target;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class ChartEntryWithBase extends BaseChartEntry {
|
||||
|
||||
private final String recordedAt;
|
||||
private final String city;
|
||||
private final int position;
|
||||
|
||||
public ChartEntryWithBase(String chartName, String songTitle, String artistName, String recordedAt,
|
||||
String city, int position) {
|
||||
super( chartName, songTitle, artistName );
|
||||
this.recordedAt = recordedAt;
|
||||
this.city = city;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public String getRecordedAt() {
|
||||
return recordedAt;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource._target;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class ChartEntryWithMapping {
|
||||
|
||||
private final String chartName;
|
||||
private final String songTitle;
|
||||
private final int artistId;
|
||||
private final String recordedAt;
|
||||
private final String city;
|
||||
private final int position;
|
||||
|
||||
public ChartEntryWithMapping(String chartName, String songTitle, int artistId, String recordedAt,
|
||||
String city, int position) {
|
||||
this.chartName = chartName;
|
||||
this.songTitle = songTitle;
|
||||
this.artistId = artistId;
|
||||
this.recordedAt = recordedAt;
|
||||
this.city = city;
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public String getChartName() {
|
||||
return chartName;
|
||||
}
|
||||
|
||||
public String getSongTitle() {
|
||||
return songTitle;
|
||||
}
|
||||
|
||||
public int getArtistId() {
|
||||
return artistId;
|
||||
}
|
||||
|
||||
public String getRecordedAt() {
|
||||
return recordedAt;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource._target;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class ChartPositions {
|
||||
|
||||
private final List<Long> positions;
|
||||
|
||||
public ChartPositions(List<Long> positions) {
|
||||
this.positions = positions;
|
||||
}
|
||||
|
||||
public List<Long> getPositions() {
|
||||
return Collections.unmodifiableList( positions );
|
||||
}
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource.source;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class Artist {
|
||||
|
||||
private final String name;
|
||||
private final Label label;
|
||||
|
||||
public Artist(String name, Label label) {
|
||||
this.name = name;
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Label getLabel() {
|
||||
return label;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource.source;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class Chart {
|
||||
|
||||
private final String type;
|
||||
private final String name;
|
||||
private final Song song;
|
||||
|
||||
public Chart(String type, String name, Song song) {
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.song = song;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Song getSong() {
|
||||
return song;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource.source;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class Label {
|
||||
|
||||
private final String name;
|
||||
private final Studio studio;
|
||||
|
||||
public Label(String name, Studio studio) {
|
||||
this.name = name;
|
||||
this.studio = studio;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Studio getStudio() {
|
||||
return studio;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource.source;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class Song {
|
||||
|
||||
private final Artist artist;
|
||||
private final String title;
|
||||
private final List<Integer> positions;
|
||||
|
||||
public Song(Artist artist, String title, List<Integer> positions) {
|
||||
this.artist = artist;
|
||||
this.title = title;
|
||||
this.positions = positions;
|
||||
}
|
||||
|
||||
public Artist getArtist() {
|
||||
return artist;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public List<Integer> getPositions() {
|
||||
return positions;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedsource.source;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class Studio {
|
||||
|
||||
private final String name;
|
||||
private final String city;
|
||||
|
||||
public Studio(String name, String city) {
|
||||
this.name = name;
|
||||
this.city = city;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public String getCity() {
|
||||
return city;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedtarget;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.mapstruct.InheritInverseConfiguration;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Chart;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@Mapper
|
||||
public abstract class ChartEntryToArtist {
|
||||
|
||||
public static final ChartEntryToArtist MAPPER = Mappers.getMapper( ChartEntryToArtist.class );
|
||||
|
||||
@Mappings({
|
||||
@Mapping(target = "type", ignore = true),
|
||||
@Mapping(target = "name", source = "chartName"),
|
||||
@Mapping(target = "song.title", source = "songTitle"),
|
||||
@Mapping(target = "song.artist.name", source = "artistName"),
|
||||
@Mapping(target = "song.artist.label.studio.name", source = "recordedAt"),
|
||||
@Mapping(target = "song.artist.label.studio.city", source = "city"),
|
||||
@Mapping(target = "song.positions", source = "position")
|
||||
})
|
||||
public abstract Chart map(ChartEntry chartEntry);
|
||||
|
||||
@InheritInverseConfiguration
|
||||
public abstract ChartEntry map(Chart chart);
|
||||
|
||||
protected List<Integer> mapPosition(Integer in) {
|
||||
if ( in != null ) {
|
||||
return new ArrayList<>( Arrays.asList( in ) );
|
||||
}
|
||||
else {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
}
|
||||
|
||||
protected Integer mapPosition(List<Integer> in) {
|
||||
if ( in != null && !in.isEmpty() ) {
|
||||
return in.get( 0 );
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.nestedtarget;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource._target.ChartEntry;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Artist;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Chart;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Label;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Song;
|
||||
import org.mapstruct.ap.test.constructor.nestedsource.source.Studio;
|
||||
import org.mapstruct.ap.testutil.IssueKey;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
import org.mapstruct.ap.testutil.runner.GeneratedSource;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@WithClasses({
|
||||
Song.class,
|
||||
Artist.class,
|
||||
Chart.class,
|
||||
Label.class,
|
||||
Studio.class,
|
||||
ChartEntry.class,
|
||||
ChartEntryToArtist.class,
|
||||
})
|
||||
@IssueKey("73")
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class NestedProductPropertiesConstructorTest {
|
||||
|
||||
@Rule
|
||||
public GeneratedSource generatedSource = new GeneratedSource().addComparisonToFixtureFor(
|
||||
ChartEntryToArtist.class
|
||||
);
|
||||
|
||||
@Test
|
||||
public void shouldMapNestedTarget() {
|
||||
|
||||
ChartEntry chartEntry = new ChartEntry(
|
||||
"US Billboard Hot Rock Songs",
|
||||
"Purple Rain",
|
||||
"Prince",
|
||||
"Live, First Avenue, Minneapolis",
|
||||
"Minneapolis",
|
||||
1
|
||||
);
|
||||
|
||||
Chart result = ChartEntryToArtist.MAPPER.map( chartEntry );
|
||||
|
||||
assertThat( result.getName() ).isEqualTo( "US Billboard Hot Rock Songs" );
|
||||
assertThat( result.getSong() ).isNotNull();
|
||||
assertThat( result.getSong().getArtist() ).isNotNull();
|
||||
assertThat( result.getSong().getTitle() ).isEqualTo( "Purple Rain" );
|
||||
assertThat( result.getSong().getArtist().getName() ).isEqualTo( "Prince" );
|
||||
assertThat( result.getSong().getArtist().getLabel() ).isNotNull();
|
||||
assertThat( result.getSong().getArtist().getLabel().getStudio() ).isNotNull();
|
||||
assertThat( result.getSong().getArtist().getLabel().getStudio().getName() )
|
||||
.isEqualTo( "Live, First Avenue, Minneapolis" );
|
||||
assertThat( result.getSong().getArtist().getLabel().getStudio().getCity() )
|
||||
.isEqualTo( "Minneapolis" );
|
||||
assertThat( result.getSong().getPositions() ).hasSize( 1 );
|
||||
assertThat( result.getSong().getPositions().get( 0 ) ).isEqualTo( 1 );
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldReverseNestedTarget() {
|
||||
|
||||
ChartEntry chartEntry = new ChartEntry(
|
||||
"US Billboard Hot Rock Songs",
|
||||
"Purple Rain",
|
||||
"Prince",
|
||||
"Live, First Avenue, Minneapolis",
|
||||
"Minneapolis",
|
||||
1
|
||||
);
|
||||
|
||||
Chart chart = ChartEntryToArtist.MAPPER.map( chartEntry );
|
||||
ChartEntry result = ChartEntryToArtist.MAPPER.map( chart );
|
||||
|
||||
assertThat( result ).isNotNull();
|
||||
assertThat( result.getArtistName() ).isEqualTo( "Prince" );
|
||||
assertThat( result.getChartName() ).isEqualTo( "US Billboard Hot Rock Songs" );
|
||||
assertThat( result.getCity() ).isEqualTo( "Minneapolis" );
|
||||
assertThat( result.getPosition() ).isEqualTo( 1 );
|
||||
assertThat( result.getRecordedAt() ).isEqualTo( "Live, First Avenue, Minneapolis" );
|
||||
assertThat( result.getSongTitle() ).isEqualTo( "Purple Rain" );
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.simple;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.ap.test.constructor.Person;
|
||||
import org.mapstruct.ap.test.constructor.PersonDto;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper
|
||||
public interface SimpleConstructorMapper {
|
||||
|
||||
SimpleConstructorMapper INSTANCE = Mappers.getMapper( SimpleConstructorMapper.class );
|
||||
|
||||
Person map(PersonDto dto);
|
||||
|
||||
@Mapping(target = "age", constant = "25")
|
||||
@Mapping(target = "job", constant = "Software Developer")
|
||||
Person mapWithConstants(PersonDto dto);
|
||||
|
||||
@Mapping(target = "age", expression = "java(25 - 5)")
|
||||
@Mapping(target = "job", expression = "java(\"Software Developer\".toLowerCase())")
|
||||
Person mapWithExpression(PersonDto dto);
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.simple;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.test.constructor.Person;
|
||||
import org.mapstruct.ap.test.constructor.PersonDto;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@WithClasses({
|
||||
Person.class,
|
||||
PersonDto.class,
|
||||
SimpleConstructorMapper.class
|
||||
})
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class SimpleConstructorTest {
|
||||
|
||||
@Test
|
||||
public void mapDefault() {
|
||||
PersonDto source = new PersonDto();
|
||||
source.setName( "Bob" );
|
||||
source.setAge( 30 );
|
||||
source.setJob( "Software Engineer" );
|
||||
source.setCity( "Zurich" );
|
||||
source.setAddress( "Plaza 1" );
|
||||
source.setChildren( Arrays.asList( "Alice", "Tom" ) );
|
||||
|
||||
Person target = SimpleConstructorMapper.INSTANCE.map( source );
|
||||
|
||||
assertThat( target.getName() ).isEqualTo( "Bob" );
|
||||
assertThat( target.getAge() ).isEqualTo( 30 );
|
||||
assertThat( target.getJob() ).isEqualTo( "Software Engineer" );
|
||||
assertThat( target.getCity() ).isEqualTo( "Zurich" );
|
||||
assertThat( target.getAddress() ).isEqualTo( "Plaza 1" );
|
||||
assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapWithConstants() {
|
||||
PersonDto source = new PersonDto();
|
||||
source.setName( "Bob" );
|
||||
source.setAge( 30 );
|
||||
source.setJob( "Software Engineer" );
|
||||
source.setCity( "Zurich" );
|
||||
source.setAddress( "Plaza 1" );
|
||||
source.setChildren( Arrays.asList( "Alice", "Tom" ) );
|
||||
|
||||
Person target = SimpleConstructorMapper.INSTANCE.mapWithConstants( source );
|
||||
|
||||
assertThat( target.getName() ).isEqualTo( "Bob" );
|
||||
assertThat( target.getAge() ).isEqualTo( 25 );
|
||||
assertThat( target.getJob() ).isEqualTo( "Software Developer" );
|
||||
assertThat( target.getCity() ).isEqualTo( "Zurich" );
|
||||
assertThat( target.getAddress() ).isEqualTo( "Plaza 1" );
|
||||
assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" );
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mapWithExpressions() {
|
||||
PersonDto source = new PersonDto();
|
||||
source.setName( "Bob" );
|
||||
source.setAge( 30 );
|
||||
source.setJob( "Software Engineer" );
|
||||
source.setCity( "Zurich" );
|
||||
source.setAddress( "Plaza 1" );
|
||||
source.setChildren( Arrays.asList( "Alice", "Tom" ) );
|
||||
|
||||
Person target = SimpleConstructorMapper.INSTANCE.mapWithExpression( source );
|
||||
|
||||
assertThat( target.getName() ).isEqualTo( "Bob" );
|
||||
assertThat( target.getAge() ).isEqualTo( 20 );
|
||||
assertThat( target.getJob() ).isEqualTo( "software developer" );
|
||||
assertThat( target.getCity() ).isEqualTo( "Zurich" );
|
||||
assertThat( target.getAddress() ).isEqualTo( "Plaza 1" );
|
||||
assertThat( target.getChildren() ).containsExactly( "Alice", "Tom" );
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.unmapped;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class Order {
|
||||
|
||||
private final String name;
|
||||
private final Date time;
|
||||
|
||||
public Order(String name, Date time) {
|
||||
this.name = name;
|
||||
this.time = time;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public Date getTime() {
|
||||
return time;
|
||||
}
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.unmapped;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class OrderDto {
|
||||
|
||||
private final String name;
|
||||
|
||||
public OrderDto(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.unmapped;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper
|
||||
public interface UnmappedConstructorMapper {
|
||||
|
||||
UnmappedConstructorMapper INSTANCE = Mappers.getMapper( UnmappedConstructorMapper.class );
|
||||
|
||||
Order map(OrderDto dto);
|
||||
}
|
@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.constructor.unmapped;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.ap.testutil.WithClasses;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
|
||||
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@WithClasses({
|
||||
Order.class,
|
||||
OrderDto.class,
|
||||
UnmappedConstructorMapper.class
|
||||
})
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class UnmappedConstructorTest {
|
||||
|
||||
@Test
|
||||
@ExpectedCompilationOutcome(value = CompilationResult.SUCCEEDED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = UnmappedConstructorMapper.class,
|
||||
kind = javax.tools.Diagnostic.Kind.WARNING,
|
||||
line = 19,
|
||||
message = "Unmapped target property: \"time\".")
|
||||
})
|
||||
public void shouldGenerateCompilableCodeForUnmappedConstructorProperties() {
|
||||
OrderDto source = new OrderDto( "truck" );
|
||||
|
||||
Order target = UnmappedConstructorMapper.INSTANCE.map( source );
|
||||
|
||||
assertThat( target.getName() ).isEqualTo( "truck" );
|
||||
assertThat( target.getTime() ).isNull();
|
||||
}
|
||||
}
|
@ -36,12 +36,7 @@ public class AmbiguousAnnotatedFactoryTest {
|
||||
".ambiguousannotatedfactorymethod.Foo foo), org.mapstruct.ap.test.erroneous" +
|
||||
".ambiguousannotatedfactorymethod.Bar org.mapstruct.ap.test.erroneous" +
|
||||
".ambiguousannotatedfactorymethod.AmbiguousBarFactory.createBar(org.mapstruct.ap.test.erroneous" +
|
||||
".ambiguousannotatedfactorymethod.Foo foo)."),
|
||||
@Diagnostic(type = SourceTargetMapperAndBarFactory.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 22,
|
||||
message = "org.mapstruct.ap.test.erroneous.ambiguousannotatedfactorymethod.Bar does not have an " +
|
||||
"accessible parameterless constructor.")
|
||||
".ambiguousannotatedfactorymethod.Foo foo).")
|
||||
|
||||
}
|
||||
)
|
||||
|
@ -37,12 +37,7 @@ public class FactoryTest {
|
||||
message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.erroneous" +
|
||||
".ambiguousfactorymethod.Bar: org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar " +
|
||||
"createBar(), org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar org.mapstruct.ap.test" +
|
||||
".erroneous.ambiguousfactorymethod.a.BarFactory.createBar()."),
|
||||
@Diagnostic(type = SourceTargetMapperAndBarFactory.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 22,
|
||||
message = "org.mapstruct.ap.test.erroneous.ambiguousfactorymethod.Bar does not have an accessible " +
|
||||
"parameterless constructor.")
|
||||
".erroneous.ambiguousfactorymethod.a.BarFactory.createBar().")
|
||||
|
||||
}
|
||||
)
|
||||
|
@ -28,9 +28,22 @@ public interface ArtistToChartEntryErroneous {
|
||||
@Mapping(target = "city", ignore = true),
|
||||
@Mapping(target = "position", source = "position")
|
||||
} )
|
||||
ChartEntry forward(Integer position);
|
||||
ChartEntry forward(ChartPosition position);
|
||||
|
||||
@InheritInverseConfiguration
|
||||
Integer reverse(ChartEntry position);
|
||||
ChartPosition reverse(ChartEntry position);
|
||||
|
||||
class ChartPosition {
|
||||
|
||||
private final int position;
|
||||
|
||||
private ChartPosition(int position) {
|
||||
this.position = position;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -164,17 +164,18 @@ public class NestedSourcePropertiesTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@IssueKey( "838" )
|
||||
@IssueKey("838")
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic( type = ArtistToChartEntryErroneous.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 34,
|
||||
message = "java.lang.Integer does not have an accessible parameterless constructor." )
|
||||
}
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = ArtistToChartEntryErroneous.class,
|
||||
kind = javax.tools.Diagnostic.Kind.ERROR,
|
||||
line = 34,
|
||||
message = "org.mapstruct.ap.test.nestedsourceproperties.ArtistToChartEntryErroneous.ChartPosition " +
|
||||
"does not have an accessible constructor.")
|
||||
}
|
||||
)
|
||||
@WithClasses({ ArtistToChartEntryErroneous.class })
|
||||
public void inverseShouldRaiseErrorForEmptyConstructor() {
|
||||
public void inverseShouldRaiseErrorForNotAccessibleConstructor() {
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.resulttype;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
public class Citrus extends Fruit implements IsFruit {
|
||||
|
||||
public Citrus(String type) {
|
||||
super( type );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setType(String type) {
|
||||
throw new RuntimeException( "Not allowed to change citrus type" );
|
||||
}
|
||||
}
|
@ -13,9 +13,16 @@ import org.mapstruct.Mapping;
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper
|
||||
public interface ErroneousResultTypeNoEmptyConstructorMapper {
|
||||
public interface ErroneousResultTypeNoAccessibleConstructorMapper {
|
||||
|
||||
@BeanMapping(resultType = Banana.class)
|
||||
@Mapping(target = "type", ignore = true)
|
||||
Fruit map(FruitDto source);
|
||||
|
||||
class Banana extends Fruit {
|
||||
|
||||
private Banana(String type) {
|
||||
super( type );
|
||||
}
|
||||
}
|
||||
}
|
@ -21,9 +21,9 @@ import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutco
|
||||
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sjaak Derksen
|
||||
*/
|
||||
@IssueKey("385")
|
||||
@ -48,12 +48,7 @@ public class InheritanceSelectionTest {
|
||||
message = "Ambiguous factory methods found for creating org.mapstruct.ap.test.selection.resulttype" +
|
||||
".Fruit: org.mapstruct.ap.test.selection.resulttype.Apple org.mapstruct.ap.test.selection" +
|
||||
".resulttype.ConflictingFruitFactory.createApple(), org.mapstruct.ap.test.selection.resulttype" +
|
||||
".Banana org.mapstruct.ap.test.selection.resulttype.ConflictingFruitFactory.createBanana()."),
|
||||
@Diagnostic(type = ErroneousFruitMapper.class,
|
||||
kind = Kind.ERROR,
|
||||
line = 23,
|
||||
message = "org.mapstruct.ap.test.selection.resulttype.Fruit does not have an accessible parameterless" +
|
||||
" constructor.")
|
||||
".Banana org.mapstruct.ap.test.selection.resulttype.ConflictingFruitFactory.createBanana().")
|
||||
}
|
||||
)
|
||||
public void testForkedInheritanceHierarchyShouldResultInAmbigousMappingMethod() {
|
||||
@ -61,22 +56,22 @@ public class InheritanceSelectionTest {
|
||||
|
||||
@IssueKey("1283")
|
||||
@Test
|
||||
@WithClasses({ ErroneousResultTypeNoEmptyConstructorMapper.class, Banana.class })
|
||||
@WithClasses({ ErroneousResultTypeNoAccessibleConstructorMapper.class })
|
||||
@ExpectedCompilationOutcome(
|
||||
value = CompilationResult.FAILED,
|
||||
diagnostics = {
|
||||
@Diagnostic(type = ErroneousResultTypeNoEmptyConstructorMapper.class,
|
||||
@Diagnostic(type = ErroneousResultTypeNoAccessibleConstructorMapper.class,
|
||||
kind = Kind.ERROR,
|
||||
line = 18,
|
||||
message = "org.mapstruct.ap.test.selection.resulttype.Banana does not have an accessible " +
|
||||
"parameterless constructor.")
|
||||
message = "org.mapstruct.ap.test.selection.resulttype" +
|
||||
".ErroneousResultTypeNoAccessibleConstructorMapper.Banana does not have an accessible constructor.")
|
||||
}
|
||||
)
|
||||
public void testResultTypeHasNoSuitableEmptyConstructor() {
|
||||
public void testResultTypeHasNoSuitableAccessibleConstructor() {
|
||||
}
|
||||
|
||||
@Test
|
||||
@WithClasses( { ConflictingFruitFactory.class, ResultTypeSelectingFruitMapper.class, Banana.class } )
|
||||
@WithClasses({ ConflictingFruitFactory.class, ResultTypeSelectingFruitMapper.class, Banana.class })
|
||||
public void testResultTypeBasedFactoryMethodSelection() {
|
||||
|
||||
FruitDto fruitDto = new FruitDto( null );
|
||||
@ -88,7 +83,7 @@ public class InheritanceSelectionTest {
|
||||
|
||||
@Test
|
||||
@IssueKey("434")
|
||||
@WithClasses( { ResultTypeConstructingFruitMapper.class } )
|
||||
@WithClasses({ ResultTypeConstructingFruitMapper.class })
|
||||
public void testResultTypeBasedConstructionOfResult() {
|
||||
|
||||
FruitDto fruitDto = new FruitDto( null );
|
||||
@ -99,7 +94,7 @@ public class InheritanceSelectionTest {
|
||||
|
||||
@Test
|
||||
@IssueKey("657")
|
||||
@WithClasses( { ResultTypeConstructingFruitInterfaceMapper.class } )
|
||||
@WithClasses({ ResultTypeConstructingFruitInterfaceMapper.class })
|
||||
public void testResultTypeBasedConstructionOfResultForInterface() {
|
||||
|
||||
FruitDto fruitDto = new FruitDto( null );
|
||||
@ -143,7 +138,7 @@ public class InheritanceSelectionTest {
|
||||
|
||||
@Test
|
||||
@IssueKey("433")
|
||||
@WithClasses( {
|
||||
@WithClasses({
|
||||
FruitFamilyMapper.class,
|
||||
GoldenDeliciousDto.class,
|
||||
GoldenDelicious.class,
|
||||
@ -151,11 +146,11 @@ public class InheritanceSelectionTest {
|
||||
AppleFamilyDto.class,
|
||||
AppleFactory.class,
|
||||
Banana.class
|
||||
} )
|
||||
})
|
||||
public void testShouldSelectResultTypeInCaseOfAmbiguity() {
|
||||
|
||||
AppleFamilyDto appleFamilyDto = new AppleFamilyDto();
|
||||
appleFamilyDto.setApple( new AppleDto("AppleDto") );
|
||||
appleFamilyDto.setApple( new AppleDto( "AppleDto" ) );
|
||||
|
||||
AppleFamily result = FruitFamilyMapper.INSTANCE.map( appleFamilyDto );
|
||||
assertThat( result ).isNotNull();
|
||||
@ -167,7 +162,7 @@ public class InheritanceSelectionTest {
|
||||
|
||||
@Test
|
||||
@IssueKey("433")
|
||||
@WithClasses( {
|
||||
@WithClasses({
|
||||
FruitFamilyMapper.class,
|
||||
GoldenDeliciousDto.class,
|
||||
GoldenDelicious.class,
|
||||
@ -175,7 +170,7 @@ public class InheritanceSelectionTest {
|
||||
AppleFamilyDto.class,
|
||||
AppleFactory.class,
|
||||
Banana.class
|
||||
} )
|
||||
})
|
||||
public void testShouldSelectResultTypeInCaseOfAmbiguityForIterable() {
|
||||
|
||||
List<AppleDto> source = Arrays.asList( new AppleDto( "AppleDto" ) );
|
||||
@ -189,7 +184,7 @@ public class InheritanceSelectionTest {
|
||||
|
||||
@Test
|
||||
@IssueKey("433")
|
||||
@WithClasses( {
|
||||
@WithClasses({
|
||||
FruitFamilyMapper.class,
|
||||
GoldenDeliciousDto.class,
|
||||
GoldenDelicious.class,
|
||||
@ -197,7 +192,7 @@ public class InheritanceSelectionTest {
|
||||
AppleFamilyDto.class,
|
||||
AppleFactory.class,
|
||||
Banana.class
|
||||
} )
|
||||
})
|
||||
public void testShouldSelectResultTypeInCaseOfAmbiguityForMap() {
|
||||
|
||||
Map<AppleDto, AppleDto> source = new HashMap<AppleDto, AppleDto>();
|
||||
@ -214,4 +209,38 @@ public class InheritanceSelectionTest {
|
||||
assertThat( entry.getValue().getType() ).isEqualTo( "AppleDto" );
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
@IssueKey("73")
|
||||
@WithClasses({
|
||||
Citrus.class,
|
||||
ResultTypeWithConstructorConstructingFruitInterfaceMapper.class
|
||||
})
|
||||
public void testShouldUseConstructorFromResultTypeForInterface() {
|
||||
FruitDto orange = new FruitDto( "orange" );
|
||||
IsFruit citrus = ResultTypeWithConstructorConstructingFruitInterfaceMapper.INSTANCE.map( orange );
|
||||
|
||||
assertThat( citrus ).isInstanceOf( Citrus.class );
|
||||
assertThat( citrus.getType() ).isEqualTo( "orange" );
|
||||
assertThatThrownBy( () -> citrus.setType( "lemon" ) )
|
||||
.hasMessage( "Not allowed to change citrus type" );
|
||||
assertThat( citrus.getType() ).isEqualTo( "orange" );
|
||||
}
|
||||
|
||||
@Test
|
||||
@IssueKey("73")
|
||||
@WithClasses({
|
||||
Citrus.class,
|
||||
ResultTypeWithConstructorConstructingFruitInterfaceMapper.class
|
||||
})
|
||||
public void testShouldUseConstructorFromResultType() {
|
||||
FruitDto lemon = new FruitDto( "lemon" );
|
||||
IsFruit citrus = ResultTypeWithConstructorConstructingFruitInterfaceMapper.INSTANCE.map( lemon );
|
||||
|
||||
assertThat( citrus ).isInstanceOf( Citrus.class );
|
||||
assertThat( citrus.getType() ).isEqualTo( "lemon" );
|
||||
assertThatThrownBy( () -> citrus.setType( "orange" ) )
|
||||
.hasMessage( "Not allowed to change citrus type" );
|
||||
assertThat( citrus.getType() ).isEqualTo( "lemon" );
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.resulttype;
|
||||
|
||||
import org.mapstruct.BeanMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper
|
||||
public interface ResultTypeWithConstructorConstructingFruitInterfaceMapper {
|
||||
|
||||
ResultTypeWithConstructorConstructingFruitInterfaceMapper INSTANCE = Mappers.getMapper(
|
||||
ResultTypeWithConstructorConstructingFruitInterfaceMapper.class );
|
||||
|
||||
@BeanMapping(resultType = Citrus.class)
|
||||
IsFruit map(FruitDto source);
|
||||
}
|
@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright MapStruct Authors.
|
||||
*
|
||||
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*/
|
||||
package org.mapstruct.ap.test.selection.resulttype;
|
||||
|
||||
import org.mapstruct.BeanMapping;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
/**
|
||||
* @author Filip Hrisafov
|
||||
*/
|
||||
@Mapper
|
||||
public interface ResultTypeWithConstructorConstructingFruitMapper {
|
||||
|
||||
ResultTypeWithConstructorConstructingFruitMapper INSTANCE = Mappers.getMapper(
|
||||
ResultTypeWithConstructorConstructingFruitMapper.class );
|
||||
|
||||
@BeanMapping(resultType = Citrus.class)
|
||||
Fruit map(FruitDto source);
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user