diff --git a/core/src/main/java/de/moapa/maple/Mapping.java b/core/src/main/java/de/moapa/maple/Mapping.java index 2efd59c6b..36b675d32 100644 --- a/core/src/main/java/de/moapa/maple/Mapping.java +++ b/core/src/main/java/de/moapa/maple/Mapping.java @@ -17,4 +17,8 @@ package de.moapa.maple; public @interface Mapping { + String source(); + + String target(); + } diff --git a/processor/src/main/java/de/moapa/maple/ap/MapperGenerationVisitor.java b/processor/src/main/java/de/moapa/maple/ap/MapperGenerationVisitor.java index 3100a64cc..2b62b8019 100644 --- a/processor/src/main/java/de/moapa/maple/ap/MapperGenerationVisitor.java +++ b/processor/src/main/java/de/moapa/maple/ap/MapperGenerationVisitor.java @@ -18,14 +18,19 @@ package de.moapa.maple.ap; import java.io.BufferedWriter; import java.util.ArrayList; import java.util.List; +import java.util.Map.Entry; 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.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.element.VariableElement; import javax.lang.model.util.ElementKindVisitor6; +import javax.lang.model.util.SimpleAnnotationValueVisitor6; import javax.tools.JavaFileObject; +import de.moapa.maple.ap.model.Binding; import de.moapa.maple.ap.model.Mapper; import de.moapa.maple.ap.model.MapperMethod; import de.moapa.maple.ap.model.Parameter; @@ -35,12 +40,14 @@ import freemarker.template.Template; import static javax.lang.model.util.ElementFilter.methodsIn; -class MapperGenerationVisitor extends ElementKindVisitor6 { +public class MapperGenerationVisitor extends ElementKindVisitor6 { private final static String IMPLEMENTATION_SUFFIX = "Impl"; 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 Configuration configuration; @@ -92,11 +99,15 @@ class MapperGenerationVisitor extends ElementKindVisitor6 { for ( ExecutableElement oneMethod : methodsIn( element.getEnclosedElements() ) ) { + Type returnType = retrieveReturnType( oneMethod ); + Parameter parameter = retrieveParameter( oneMethod ); + List bindings = retrieveBindings( oneMethod ); methods.add( new MapperMethod( oneMethod.getSimpleName().toString(), - retrieveReturnType( oneMethod ), - retrieveParameter( oneMethod ) + returnType, + parameter, + bindings ) ); } @@ -104,6 +115,43 @@ class MapperGenerationVisitor extends ElementKindVisitor6 { return methods; } + private List retrieveBindings(ExecutableElement method) { + + List bindings = new ArrayList(); + + 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 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) { List parameters = method.getParameters(); @@ -137,4 +185,21 @@ class MapperGenerationVisitor extends ElementKindVisitor6 { returnTypeElement.getSimpleName().toString() ); } -} \ No newline at end of file + + private static class NameDeterminationVisitor extends ElementKindVisitor6 { + + @Override + public String visitType(TypeElement element, Void p) { + return element.getQualifiedName().toString(); + } + } + + private static class ValueRetrievingVisitor extends SimpleAnnotationValueVisitor6 { + + @Override + public String visitString(String value, Void p) { + return value; + } + + } +} diff --git a/processor/src/main/java/de/moapa/maple/ap/model/Binding.java b/processor/src/main/java/de/moapa/maple/ap/model/Binding.java new file mode 100644 index 000000000..19b0b7252 --- /dev/null +++ b/processor/src/main/java/de/moapa/maple/ap/model/Binding.java @@ -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; + } +} diff --git a/processor/src/main/java/de/moapa/maple/ap/model/MapperMethod.java b/processor/src/main/java/de/moapa/maple/ap/model/MapperMethod.java index 01137012c..7029aae22 100644 --- a/processor/src/main/java/de/moapa/maple/ap/model/MapperMethod.java +++ b/processor/src/main/java/de/moapa/maple/ap/model/MapperMethod.java @@ -15,16 +15,20 @@ */ package de.moapa.maple.ap.model; +import java.util.List; + public class MapperMethod { private final String name; private final Type returnType; private final Parameter parameter; + private final List bindings; - public MapperMethod(String name, Type returnType, Parameter parameter) { + public MapperMethod(String name, Type returnType, Parameter parameter, List bindings) { this.name = name; this.returnType = returnType; this.parameter = parameter; + this.bindings = bindings; } public String getName() { @@ -38,4 +42,8 @@ public class MapperMethod { public Parameter getParameter() { return parameter; } + + public List getBindings() { + return bindings; + } } diff --git a/processor/src/main/resources/dozer-mapper-implementation.ftl b/processor/src/main/resources/dozer-mapper-implementation.ftl index 9927ec77f..610942830 100644 --- a/processor/src/main/resources/dozer-mapper-implementation.ftl +++ b/processor/src/main/resources/dozer-mapper-implementation.ftl @@ -18,14 +18,28 @@ package ${packageName}; import org.dozer.DozerBeanMapper; -import org.dozer.Mapper; +import org.dozer.loader.api.BeanMappingBuilder; public class ${implementationType} implements ${interfaceType} { -private final Mapper mapper; +private final DozerBeanMapper mapper; public ${implementationType}() { 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}") + +; +} +}; + +mapper.addMapping( builder ); + } <#list mapperMethods as oneMethod> diff --git a/processor/src/test/java/de/moapa/maple/ap/test/CarMapperTest.java b/processor/src/test/java/de/moapa/maple/ap/test/CarMapperTest.java index 3f3434395..1f3b46aed 100644 --- a/processor/src/test/java/de/moapa/maple/ap/test/CarMapperTest.java +++ b/processor/src/test/java/de/moapa/maple/ap/test/CarMapperTest.java @@ -80,43 +80,51 @@ public class CarMapperTest { } @BeforeMethod - public void setUpDiagnostics() { + public void generateMapperImplementation() { + diagnostics = new DiagnosticCollector(); + File[] sourceFiles = getSourceFiles( Car.class, CarDto.class, CarMapper.class ); + + boolean compilationSuccessful = compile( diagnostics, sourceFiles ); + + assertThat( compilationSuccessful ).describedAs( "Compilation failed: " + diagnostics.getDiagnostics() ) + .isTrue(); } @Test 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(); } @Test - public void shouldMapCar() { + public void shouldMapAttributeByName() { //given - File[] sourceFiles = getSourceFiles( Car.class, CarDto.class, CarMapper.class ); - Car car = new Car( "Morris" ); + Car car = new Car( "Morris", 2 ); //when - boolean compilationSuccessful = compile( diagnostics, sourceFiles ); CarDto carDto = CarMapper.INSTANCE.carToCarDto( car ); //then - assertThat( compilationSuccessful ).describedAs( "Compilation failed: " + diagnostics.getDiagnostics() ) - .isTrue(); assertThat( carDto ).isNotNull(); 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) { File[] sourceFiles = new File[clazz.length]; diff --git a/processor/src/test/java/de/moapa/maple/ap/test/model/Car.java b/processor/src/test/java/de/moapa/maple/ap/test/model/Car.java index 000762f6a..2d4d00dfd 100644 --- a/processor/src/test/java/de/moapa/maple/ap/test/model/Car.java +++ b/processor/src/test/java/de/moapa/maple/ap/test/model/Car.java @@ -19,8 +19,11 @@ public class Car { private String make; - public Car(String make) { + private int numberOfSeats; + + public Car(String make, int numberOfSeats) { this.make = make; + this.numberOfSeats = numberOfSeats; } public String getMake() { @@ -30,4 +33,12 @@ public class Car { public void setMake(String make) { this.make = make; } + + public int getNumberOfSeats() { + return numberOfSeats; + } + + public void setNumberOfSeats(int numberOfSeats) { + this.numberOfSeats = numberOfSeats; + } } diff --git a/processor/src/test/java/de/moapa/maple/ap/test/model/CarDto.java b/processor/src/test/java/de/moapa/maple/ap/test/model/CarDto.java index 0044380a0..62a410745 100644 --- a/processor/src/test/java/de/moapa/maple/ap/test/model/CarDto.java +++ b/processor/src/test/java/de/moapa/maple/ap/test/model/CarDto.java @@ -19,11 +19,9 @@ public class CarDto { private String make; - public CarDto() { - } + private int seatCount; - public CarDto(String make) { - this.make = make; + public CarDto() { } public String getMake() { @@ -33,4 +31,12 @@ public class CarDto { public void setMake(String make) { this.make = make; } + + public int getSeatCount() { + return seatCount; + } + + public void setSeatCount(int seatCount) { + this.seatCount = seatCount; + } } diff --git a/processor/src/test/java/de/moapa/maple/ap/test/model/CarMapper.java b/processor/src/test/java/de/moapa/maple/ap/test/model/CarMapper.java index 2246b5160..759a3c025 100644 --- a/processor/src/test/java/de/moapa/maple/ap/test/model/CarMapper.java +++ b/processor/src/test/java/de/moapa/maple/ap/test/model/CarMapper.java @@ -17,11 +17,13 @@ package de.moapa.maple.ap.test.model; import de.moapa.maple.Mapper; import de.moapa.maple.Mappers; +import de.moapa.maple.Mapping; @Mapper public interface CarMapper { CarMapper INSTANCE = Mappers.getMapper( CarMapper.class ); + @Mapping(source = "numberOfSeats", target = "seatCount") CarDto carToCarDto(Car car); }