mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
Adding simple binding support
This commit is contained in:
parent
61f181a589
commit
1cf7e4275e
@ -17,4 +17,8 @@ package de.moapa.maple;
|
|||||||
|
|
||||||
public @interface Mapping {
|
public @interface Mapping {
|
||||||
|
|
||||||
|
String source();
|
||||||
|
|
||||||
|
String target();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,14 +18,19 @@ package de.moapa.maple.ap;
|
|||||||
import java.io.BufferedWriter;
|
import java.io.BufferedWriter;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import javax.annotation.processing.ProcessingEnvironment;
|
import javax.annotation.processing.ProcessingEnvironment;
|
||||||
|
import javax.lang.model.element.AnnotationMirror;
|
||||||
|
import javax.lang.model.element.AnnotationValue;
|
||||||
import javax.lang.model.element.Element;
|
import javax.lang.model.element.Element;
|
||||||
import javax.lang.model.element.ExecutableElement;
|
import javax.lang.model.element.ExecutableElement;
|
||||||
import javax.lang.model.element.TypeElement;
|
import javax.lang.model.element.TypeElement;
|
||||||
import javax.lang.model.element.VariableElement;
|
import javax.lang.model.element.VariableElement;
|
||||||
import javax.lang.model.util.ElementKindVisitor6;
|
import javax.lang.model.util.ElementKindVisitor6;
|
||||||
|
import javax.lang.model.util.SimpleAnnotationValueVisitor6;
|
||||||
import javax.tools.JavaFileObject;
|
import javax.tools.JavaFileObject;
|
||||||
|
|
||||||
|
import de.moapa.maple.ap.model.Binding;
|
||||||
import de.moapa.maple.ap.model.Mapper;
|
import de.moapa.maple.ap.model.Mapper;
|
||||||
import de.moapa.maple.ap.model.MapperMethod;
|
import de.moapa.maple.ap.model.MapperMethod;
|
||||||
import de.moapa.maple.ap.model.Parameter;
|
import de.moapa.maple.ap.model.Parameter;
|
||||||
@ -35,12 +40,14 @@ import freemarker.template.Template;
|
|||||||
|
|
||||||
import static javax.lang.model.util.ElementFilter.methodsIn;
|
import static javax.lang.model.util.ElementFilter.methodsIn;
|
||||||
|
|
||||||
class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
||||||
|
|
||||||
private final static String IMPLEMENTATION_SUFFIX = "Impl";
|
private final static String IMPLEMENTATION_SUFFIX = "Impl";
|
||||||
|
|
||||||
private final static String TEMPLATE_NAME = "dozer-mapper-implementation.ftl";
|
private final static String TEMPLATE_NAME = "dozer-mapper-implementation.ftl";
|
||||||
|
|
||||||
|
private final static String MAPPING_ANNOTATION = "de.moapa.maple.Mapping";
|
||||||
|
|
||||||
private final ProcessingEnvironment processingEnvironment;
|
private final ProcessingEnvironment processingEnvironment;
|
||||||
|
|
||||||
private final Configuration configuration;
|
private final Configuration configuration;
|
||||||
@ -92,11 +99,15 @@ class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
|||||||
|
|
||||||
for ( ExecutableElement oneMethod : methodsIn( element.getEnclosedElements() ) ) {
|
for ( ExecutableElement oneMethod : methodsIn( element.getEnclosedElements() ) ) {
|
||||||
|
|
||||||
|
Type returnType = retrieveReturnType( oneMethod );
|
||||||
|
Parameter parameter = retrieveParameter( oneMethod );
|
||||||
|
List<Binding> bindings = retrieveBindings( oneMethod );
|
||||||
methods.add(
|
methods.add(
|
||||||
new MapperMethod(
|
new MapperMethod(
|
||||||
oneMethod.getSimpleName().toString(),
|
oneMethod.getSimpleName().toString(),
|
||||||
retrieveReturnType( oneMethod ),
|
returnType,
|
||||||
retrieveParameter( oneMethod )
|
parameter,
|
||||||
|
bindings
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -104,6 +115,43 @@ class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
|||||||
return methods;
|
return methods;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Binding> retrieveBindings(ExecutableElement method) {
|
||||||
|
|
||||||
|
List<Binding> bindings = new ArrayList<Binding>();
|
||||||
|
|
||||||
|
for ( AnnotationMirror annotationMirror : method.getAnnotationMirrors() ) {
|
||||||
|
|
||||||
|
String annotationName = annotationMirror.getAnnotationType()
|
||||||
|
.asElement()
|
||||||
|
.accept( new NameDeterminationVisitor(), null );
|
||||||
|
|
||||||
|
if ( MAPPING_ANNOTATION.equals( annotationName ) ) {
|
||||||
|
bindings.add( retrieveBinding( annotationMirror ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bindings;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Binding retrieveBinding(AnnotationMirror annotationMirror) {
|
||||||
|
|
||||||
|
String sourcePropertyName = null;
|
||||||
|
String targetPropertyName = null;
|
||||||
|
|
||||||
|
for ( Entry<? extends ExecutableElement, ? extends AnnotationValue> oneAttribute : annotationMirror.getElementValues()
|
||||||
|
.entrySet() ) {
|
||||||
|
|
||||||
|
if ( oneAttribute.getKey().getSimpleName().contentEquals( "source" ) ) {
|
||||||
|
sourcePropertyName = oneAttribute.getValue().accept( new ValueRetrievingVisitor(), null );
|
||||||
|
}
|
||||||
|
else if ( oneAttribute.getKey().getSimpleName().contentEquals( "target" ) ) {
|
||||||
|
targetPropertyName = oneAttribute.getValue().accept( new ValueRetrievingVisitor(), null );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Binding( sourcePropertyName, targetPropertyName );
|
||||||
|
}
|
||||||
|
|
||||||
private Parameter retrieveParameter(ExecutableElement method) {
|
private Parameter retrieveParameter(ExecutableElement method) {
|
||||||
|
|
||||||
List<? extends VariableElement> parameters = method.getParameters();
|
List<? extends VariableElement> parameters = method.getParameters();
|
||||||
@ -137,4 +185,21 @@ class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
|
|||||||
returnTypeElement.getSimpleName().toString()
|
returnTypeElement.getSimpleName().toString()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class NameDeterminationVisitor extends ElementKindVisitor6<String, Void> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitType(TypeElement element, Void p) {
|
||||||
|
return element.getQualifiedName().toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class ValueRetrievingVisitor extends SimpleAnnotationValueVisitor6<String, Void> {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String visitString(String value, Void p) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
37
processor/src/main/java/de/moapa/maple/ap/model/Binding.java
Normal file
37
processor/src/main/java/de/moapa/maple/ap/model/Binding.java
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2012 Gunnar Morling (http://www.gunnarmorling.de/)
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package de.moapa.maple.ap.model;
|
||||||
|
|
||||||
|
public class Binding {
|
||||||
|
|
||||||
|
private final String sourceProperty;
|
||||||
|
|
||||||
|
private final String targetProperty;
|
||||||
|
|
||||||
|
public Binding(String sourceProperty, String targetProperty) {
|
||||||
|
|
||||||
|
this.sourceProperty = sourceProperty;
|
||||||
|
this.targetProperty = targetProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSourceProperty() {
|
||||||
|
return sourceProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetProperty() {
|
||||||
|
return targetProperty;
|
||||||
|
}
|
||||||
|
}
|
@ -15,16 +15,20 @@
|
|||||||
*/
|
*/
|
||||||
package de.moapa.maple.ap.model;
|
package de.moapa.maple.ap.model;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class MapperMethod {
|
public class MapperMethod {
|
||||||
|
|
||||||
private final String name;
|
private final String name;
|
||||||
private final Type returnType;
|
private final Type returnType;
|
||||||
private final Parameter parameter;
|
private final Parameter parameter;
|
||||||
|
private final List<Binding> bindings;
|
||||||
|
|
||||||
public MapperMethod(String name, Type returnType, Parameter parameter) {
|
public MapperMethod(String name, Type returnType, Parameter parameter, List<Binding> bindings) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.returnType = returnType;
|
this.returnType = returnType;
|
||||||
this.parameter = parameter;
|
this.parameter = parameter;
|
||||||
|
this.bindings = bindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public String getName() {
|
||||||
@ -38,4 +42,8 @@ public class MapperMethod {
|
|||||||
public Parameter getParameter() {
|
public Parameter getParameter() {
|
||||||
return parameter;
|
return parameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Binding> getBindings() {
|
||||||
|
return bindings;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,14 +18,28 @@
|
|||||||
package ${packageName};
|
package ${packageName};
|
||||||
|
|
||||||
import org.dozer.DozerBeanMapper;
|
import org.dozer.DozerBeanMapper;
|
||||||
import org.dozer.Mapper;
|
import org.dozer.loader.api.BeanMappingBuilder;
|
||||||
|
|
||||||
public class ${implementationType} implements ${interfaceType} {
|
public class ${implementationType} implements ${interfaceType} {
|
||||||
|
|
||||||
private final Mapper mapper;
|
private final DozerBeanMapper mapper;
|
||||||
|
|
||||||
public ${implementationType}() {
|
public ${implementationType}() {
|
||||||
mapper = new DozerBeanMapper();
|
mapper = new DozerBeanMapper();
|
||||||
|
|
||||||
|
<#list mapperMethods as oneMethod>
|
||||||
|
BeanMappingBuilder builder = new BeanMappingBuilder() {
|
||||||
|
protected void configure() {
|
||||||
|
mapping( ${oneMethod.parameter.type.name}.class, ${oneMethod.returnType.name}.class )
|
||||||
|
<#list oneMethod.bindings as oneBinding>
|
||||||
|
.fields("${oneBinding.sourceProperty}", "${oneBinding.targetProperty}")
|
||||||
|
</#list>
|
||||||
|
;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
mapper.addMapping( builder );
|
||||||
|
</#list>
|
||||||
}
|
}
|
||||||
|
|
||||||
<#list mapperMethods as oneMethod>
|
<#list mapperMethods as oneMethod>
|
||||||
|
@ -80,43 +80,51 @@ public class CarMapperTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@BeforeMethod
|
@BeforeMethod
|
||||||
public void setUpDiagnostics() {
|
public void generateMapperImplementation() {
|
||||||
|
|
||||||
diagnostics = new DiagnosticCollector<JavaFileObject>();
|
diagnostics = new DiagnosticCollector<JavaFileObject>();
|
||||||
|
File[] sourceFiles = getSourceFiles( Car.class, CarDto.class, CarMapper.class );
|
||||||
|
|
||||||
|
boolean compilationSuccessful = compile( diagnostics, sourceFiles );
|
||||||
|
|
||||||
|
assertThat( compilationSuccessful ).describedAs( "Compilation failed: " + diagnostics.getDiagnostics() )
|
||||||
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldProvideMapperInstance() throws Exception {
|
public void shouldProvideMapperInstance() throws Exception {
|
||||||
|
|
||||||
//given
|
|
||||||
File[] sourceFiles = getSourceFiles( Car.class, CarDto.class, CarMapper.class );
|
|
||||||
|
|
||||||
//when
|
|
||||||
boolean compilationSuccessful = compile( diagnostics, sourceFiles );
|
|
||||||
|
|
||||||
//then
|
|
||||||
assertThat( compilationSuccessful ).describedAs( "Compilation failed: " + diagnostics.getDiagnostics() )
|
|
||||||
.isTrue();
|
|
||||||
assertThat( CarMapper.INSTANCE ).isNotNull();
|
assertThat( CarMapper.INSTANCE ).isNotNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void shouldMapCar() {
|
public void shouldMapAttributeByName() {
|
||||||
|
|
||||||
//given
|
//given
|
||||||
File[] sourceFiles = getSourceFiles( Car.class, CarDto.class, CarMapper.class );
|
Car car = new Car( "Morris", 2 );
|
||||||
Car car = new Car( "Morris" );
|
|
||||||
|
|
||||||
//when
|
//when
|
||||||
boolean compilationSuccessful = compile( diagnostics, sourceFiles );
|
|
||||||
CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );
|
CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );
|
||||||
|
|
||||||
//then
|
//then
|
||||||
assertThat( compilationSuccessful ).describedAs( "Compilation failed: " + diagnostics.getDiagnostics() )
|
|
||||||
.isTrue();
|
|
||||||
assertThat( carDto ).isNotNull();
|
assertThat( carDto ).isNotNull();
|
||||||
assertThat( carDto.getMake() ).isEqualTo( car.getMake() );
|
assertThat( carDto.getMake() ).isEqualTo( car.getMake() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldMapAttributeWithCustomMapping() {
|
||||||
|
|
||||||
|
//given
|
||||||
|
Car car = new Car( "Morris", 2 );
|
||||||
|
|
||||||
|
//when
|
||||||
|
CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );
|
||||||
|
|
||||||
|
//then
|
||||||
|
assertThat( carDto ).isNotNull();
|
||||||
|
assertThat( carDto.getSeatCount() ).isEqualTo( car.getNumberOfSeats() );
|
||||||
|
}
|
||||||
|
|
||||||
private File[] getSourceFiles(Class<?>... clazz) {
|
private File[] getSourceFiles(Class<?>... clazz) {
|
||||||
|
|
||||||
File[] sourceFiles = new File[clazz.length];
|
File[] sourceFiles = new File[clazz.length];
|
||||||
|
@ -19,8 +19,11 @@ public class Car {
|
|||||||
|
|
||||||
private String make;
|
private String make;
|
||||||
|
|
||||||
public Car(String make) {
|
private int numberOfSeats;
|
||||||
|
|
||||||
|
public Car(String make, int numberOfSeats) {
|
||||||
this.make = make;
|
this.make = make;
|
||||||
|
this.numberOfSeats = numberOfSeats;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMake() {
|
public String getMake() {
|
||||||
@ -30,4 +33,12 @@ public class Car {
|
|||||||
public void setMake(String make) {
|
public void setMake(String make) {
|
||||||
this.make = make;
|
this.make = make;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getNumberOfSeats() {
|
||||||
|
return numberOfSeats;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setNumberOfSeats(int numberOfSeats) {
|
||||||
|
this.numberOfSeats = numberOfSeats;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,11 +19,9 @@ public class CarDto {
|
|||||||
|
|
||||||
private String make;
|
private String make;
|
||||||
|
|
||||||
public CarDto() {
|
private int seatCount;
|
||||||
}
|
|
||||||
|
|
||||||
public CarDto(String make) {
|
public CarDto() {
|
||||||
this.make = make;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMake() {
|
public String getMake() {
|
||||||
@ -33,4 +31,12 @@ public class CarDto {
|
|||||||
public void setMake(String make) {
|
public void setMake(String make) {
|
||||||
this.make = make;
|
this.make = make;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getSeatCount() {
|
||||||
|
return seatCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSeatCount(int seatCount) {
|
||||||
|
this.seatCount = seatCount;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,11 +17,13 @@ package de.moapa.maple.ap.test.model;
|
|||||||
|
|
||||||
import de.moapa.maple.Mapper;
|
import de.moapa.maple.Mapper;
|
||||||
import de.moapa.maple.Mappers;
|
import de.moapa.maple.Mappers;
|
||||||
|
import de.moapa.maple.Mapping;
|
||||||
|
|
||||||
@Mapper
|
@Mapper
|
||||||
public interface CarMapper {
|
public interface CarMapper {
|
||||||
|
|
||||||
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
|
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
|
||||||
|
|
||||||
|
@Mapping(source = "numberOfSeats", target = "seatCount")
|
||||||
CarDto carToCarDto(Car car);
|
CarDto carToCarDto(Car car);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user