Adding simple binding support

This commit is contained in:
Gunnar Morling 2012-05-28 17:17:26 +02:00
parent 61f181a589
commit 1cf7e4275e
9 changed files with 183 additions and 28 deletions

View File

@ -17,4 +17,8 @@ package de.moapa.maple;
public @interface Mapping {
String source();
String target();
}

View File

@ -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<Void, Void> {
public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
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<Void, Void> {
for ( ExecutableElement oneMethod : methodsIn( element.getEnclosedElements() ) ) {
Type returnType = retrieveReturnType( oneMethod );
Parameter parameter = retrieveParameter( oneMethod );
List<Binding> 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<Void, Void> {
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) {
List<? extends VariableElement> parameters = method.getParameters();
@ -137,4 +185,21 @@ class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
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;
}
}
}

View 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;
}
}

View File

@ -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<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.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<Binding> getBindings() {
return bindings;
}
}

View File

@ -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}")
</#list>
;
}
};
mapper.addMapping( builder );
</#list>
}
<#list mapperMethods as oneMethod>

View File

@ -80,43 +80,51 @@ public class CarMapperTest {
}
@BeforeMethod
public void setUpDiagnostics() {
public void generateMapperImplementation() {
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
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];

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}