Matching mappable properties by name and type; excluding non-matching properties in native mapper

This commit is contained in:
Gunnar Morling 2012-06-16 10:51:05 +02:00
parent df25e12b0d
commit 873e091975
10 changed files with 347 additions and 34 deletions

View File

@ -45,6 +45,7 @@ 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;
import de.moapa.maple.ap.model.Property;
import de.moapa.maple.ap.model.Type;
import de.moapa.maple.ap.writer.DozerModelWriter;
import de.moapa.maple.ap.writer.ModelWriter;
@ -169,37 +170,50 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
Element returnTypeElement = typeUtils.asElement( method.getReturnType() );
Set<String> writableTargetProperties = new LinkedHashSet<String>();
Set<Property> writableTargetProperties = new LinkedHashSet<Property>();
//collect writable properties of the target type
for ( ExecutableElement oneMethod : methodsIn( returnTypeElement.getEnclosedElements() ) ) {
if ( oneMethod.getSimpleName().toString().startsWith( "set" ) &&
oneMethod.getParameters().size() == 1 ) {
writableTargetProperties.add( oneMethod.getSimpleName().toString().substring( 3 ) );
writableTargetProperties.add(
new Property(
retrieveParameter( oneMethod ).getType(),
oneMethod.getSimpleName().toString().substring( 3 )
)
);
}
}
//collect readable properties of the source type
Element parameterElement = typeUtils.asElement( method.getParameters().get( 0 ).asType() );
Set<String> readableSourceProperties = new LinkedHashSet<String>();
Set<Property> readableSourceProperties = new LinkedHashSet<Property>();
for ( ExecutableElement oneMethod : methodsIn( parameterElement.getEnclosedElements() ) ) {
//TODO: consider is/has
if ( oneMethod.getSimpleName().toString().startsWith( "get" ) &&
oneMethod.getParameters().isEmpty() &&
oneMethod.getReturnType().getKind() != TypeKind.VOID ) {
readableSourceProperties.add( oneMethod.getSimpleName().toString().substring( 3 ) );
readableSourceProperties.add(
new Property(
retrieveReturnType( oneMethod ),
oneMethod.getSimpleName().toString().substring( 3 )
)
);
}
}
writableTargetProperties.retainAll( readableSourceProperties );
for ( String oneWritableProperty : writableTargetProperties ) {
bindings.put( oneWritableProperty, new Binding( oneWritableProperty, oneWritableProperty ) );
for ( Property oneWritableProperty : writableTargetProperties ) {
bindings.put(
oneWritableProperty.getName(),
new Binding( oneWritableProperty.getName(), oneWritableProperty.getName() )
);
}
}
private void retrieveBindings(AnnotationMirror annotationMirror, Map<String, Binding> bindings) {
@ -278,21 +292,23 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
return new Parameter(
parameter.getSimpleName().toString(),
new Type(
elementUtils.getPackageOf( parameter ).getQualifiedName().toString(),
typeUtils.asElement( parameter.asType() ).getSimpleName().toString()
)
retrieveType( parameter.asType() )
);
}
private Type retrieveReturnType(ExecutableElement method) {
Element returnTypeElement = typeUtils.asElement( method.getReturnType() );
return retrieveType( method.getReturnType() );
}
return new Type(
elementUtils.getPackageOf( returnTypeElement ).getQualifiedName().toString(),
returnTypeElement.getSimpleName().toString()
);
private Type retrieveType(TypeMirror mirror) {
if ( mirror.getKind() == TypeKind.DECLARED ) {
return getType( ( (DeclaredType) mirror ).asElement() );
}
else {
return new Type( null, mirror.toString() );
}
}
private AnnotationMirror getAnnotation(TypeElement element, String annotationName) {

View File

@ -0,0 +1,85 @@
/**
* 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;
/**
* Represents a property of a Java bean.
*
* @author Gunnar Morling
*/
public class Property {
private final Type type;
private final String name;
public Property(Type type, String name) {
this.type = type;
this.name = name;
}
public Type getType() {
return type;
}
public String getName() {
return name;
}
@Override
public String toString() {
return "Property [type=" + type + ", name=" + name + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ( ( name == null ) ? 0 : name.hashCode() );
result = prime * result + ( ( type == null ) ? 0 : type.hashCode() );
return result;
}
@Override
public boolean equals(Object obj) {
if ( this == obj ) {
return true;
}
if ( obj == null ) {
return false;
}
if ( getClass() != obj.getClass() ) {
return false;
}
Property other = (Property) obj;
if ( name == null ) {
if ( other.name != null ) {
return false;
}
}
else if ( !name.equals( other.name ) ) {
return false;
}
if ( type == null ) {
if ( other.type != null ) {
return false;
}
}
else if ( !type.equals( other.type ) ) {
return false;
}
return true;
}
}

View File

@ -15,6 +15,11 @@
*/
package de.moapa.maple.ap.model;
/**
* Represents the type of a bean property, parameter etc.
*
* @author Gunnar Morling
*/
public class Type {
private final String packageName;
@ -33,4 +38,50 @@ public class Type {
public String getName() {
return name;
}
@Override
public String toString() {
return "Type [packageName=" + packageName + ", name=" + name + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ( ( name == null ) ? 0 : name.hashCode() );
result = prime * result
+ ( ( packageName == null ) ? 0 : packageName.hashCode() );
return result;
}
@Override
public boolean equals(Object obj) {
if ( this == obj ) {
return true;
}
if ( obj == null ) {
return false;
}
if ( getClass() != obj.getClass() ) {
return false;
}
Type other = (Type) obj;
if ( name == null ) {
if ( other.name != null ) {
return false;
}
}
else if ( !name.equals( other.name ) ) {
return false;
}
if ( packageName == null ) {
if ( other.packageName != null ) {
return false;
}
}
else if ( !packageName.equals( other.packageName ) ) {
return false;
}
return true;
}
}

View File

@ -18,8 +18,8 @@
package ${packageName};
public class ${implementationType} implements ${interfaceType} {
<#list mapperMethods as oneMethod>
public ${oneMethod.returnType.name} ${oneMethod.name}(${oneMethod.parameter.type.name} ${oneMethod.parameter.name}) {
${oneMethod.returnType.name} convertedObject = new ${oneMethod.returnType.name}();

View File

@ -23,6 +23,8 @@ import de.moapa.maple.ap.test.model.Car;
import de.moapa.maple.ap.test.model.CarDto;
import de.moapa.maple.ap.test.model.CarMapper;
import de.moapa.maple.ap.test.model.IntToStringConverter;
import de.moapa.maple.ap.test.model.Person;
import de.moapa.maple.ap.test.model.PersonDto;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@ -40,7 +42,14 @@ public class CarMapperTest extends MapperTestBase {
public void generateMapperImplementation() {
diagnostics = new DiagnosticCollector<JavaFileObject>();
File[] sourceFiles = getSourceFiles( Car.class, CarDto.class, CarMapper.class, IntToStringConverter.class );
File[] sourceFiles = getSourceFiles(
Car.class,
CarDto.class,
Person.class,
PersonDto.class,
CarMapper.class,
IntToStringConverter.class
);
boolean compilationSuccessful = compile( diagnostics, sourceFiles );
@ -58,7 +67,7 @@ public class CarMapperTest extends MapperTestBase {
public void shouldMapAttributeByName() {
//given
Car car = new Car( "Morris", 2, 1980 );
Car car = new Car( "Morris", 2, 1980, new Person( "Bob" ) );
//when
CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );
@ -68,11 +77,41 @@ public class CarMapperTest extends MapperTestBase {
assertThat( carDto.getMake() ).isEqualTo( car.getMake() );
}
@Test
public void shouldMapReferenceAttribute() {
//given
Car car = new Car( "Morris", 2, 1980, new Person( "Bob" ) );
//when
CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );
//then
assertThat( carDto ).isNotNull();
assertThat( carDto.getDriver() ).isNotNull();
assertThat( carDto.getDriver().getName() ).isEqualTo( "Bob" );
}
@Test
public void shouldReverseMapReferenceAttribute() {
//given
CarDto carDto = new CarDto( "Morris", 2, "1980", new PersonDto( "Bob" ) );
//when
Car car = CarMapper.INSTANCE.carDtoToCar( carDto );
//then
assertThat( car ).isNotNull();
assertThat( car.getDriver() ).isNotNull();
assertThat( car.getDriver().getName() ).isEqualTo( "Bob" );
}
@Test
public void shouldMapAttributeWithCustomMapping() {
//given
Car car = new Car( "Morris", 2, 1980 );
Car car = new Car( "Morris", 2, 1980, new Person( "Bob" ) );
//when
CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );
@ -86,7 +125,7 @@ public class CarMapperTest extends MapperTestBase {
public void shouldConsiderCustomMappingForReverseMapping() {
//given
CarDto carDto = new CarDto( "Morris", 2, "1980" );
CarDto carDto = new CarDto( "Morris", 2, "1980", new PersonDto( "Bob" ) );
//when
Car car = CarMapper.INSTANCE.carDtoToCar( carDto );
@ -100,7 +139,7 @@ public class CarMapperTest extends MapperTestBase {
public void shouldApplyConverter() {
//given
Car car = new Car( "Morris", 2, 1980 );
Car car = new Car( "Morris", 2, 1980, new Person( "Bob" ) );
//when
CarDto carDto = CarMapper.INSTANCE.carToCarDto( car );
@ -114,7 +153,7 @@ public class CarMapperTest extends MapperTestBase {
public void shouldApplyConverterForReverseMapping() {
//given
CarDto carDto = new CarDto( "Morris", 2, "1980" );
CarDto carDto = new CarDto( "Morris", 2, "1980", new PersonDto( "Bob" ) );
//when
Car car = CarMapper.INSTANCE.carDtoToCar( carDto );

View File

@ -23,6 +23,8 @@ import de.moapa.maple.ap.test.model.Car;
import de.moapa.maple.ap.test.model.CarDto;
import de.moapa.maple.ap.test.model.IntToStringConverter;
import de.moapa.maple.ap.test.model.NativeCarMapper;
import de.moapa.maple.ap.test.model.Person;
import de.moapa.maple.ap.test.model.PersonDto;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
@ -43,6 +45,8 @@ public class NativeCarMapperTest extends MapperTestBase {
File[] sourceFiles = getSourceFiles(
Car.class,
CarDto.class,
Person.class,
PersonDto.class,
NativeCarMapper.class,
IntToStringConverter.class
);
@ -63,7 +67,7 @@ public class NativeCarMapperTest extends MapperTestBase {
public void shouldMapAttributeByName() {
//given
Car car = new Car( "Morris", 2, 1980 );
Car car = new Car( "Morris", 2, 1980, new Person( "Bob" ) );
//when
CarDto carDto = NativeCarMapper.INSTANCE.carToCarDto( car );
@ -73,11 +77,41 @@ public class NativeCarMapperTest extends MapperTestBase {
assertThat( carDto.getMake() ).isEqualTo( car.getMake() );
}
@Test(enabled = false)
public void shouldMapReferenceAttribute() {
//given
Car car = new Car( "Morris", 2, 1980, new Person( "Bob" ) );
//when
CarDto carDto = NativeCarMapper.INSTANCE.carToCarDto( car );
//then
assertThat( carDto ).isNotNull();
assertThat( carDto.getDriver() ).isNotNull();
assertThat( carDto.getDriver().getName() ).isEqualTo( "Bob" );
}
@Test(enabled = false)
public void shouldReverseMapReferenceAttribute() {
//given
CarDto carDto = new CarDto( "Morris", 2, "1980", new PersonDto( "Bob" ) );
//when
Car car = NativeCarMapper.INSTANCE.carDtoToCar( carDto );
//then
assertThat( car ).isNotNull();
assertThat( car.getDriver() ).isNotNull();
assertThat( car.getDriver().getName() ).isEqualTo( "Bob" );
}
@Test
public void shouldMapAttributeWithCustomMapping() {
//given
Car car = new Car( "Morris", 2, 1980 );
Car car = new Car( "Morris", 2, 1980, new Person( "Bob" ) );
//when
CarDto carDto = NativeCarMapper.INSTANCE.carToCarDto( car );
@ -91,7 +125,7 @@ public class NativeCarMapperTest extends MapperTestBase {
public void shouldConsiderCustomMappingForReverseMapping() {
//given
CarDto carDto = new CarDto( "Morris", 2, "1980" );
CarDto carDto = new CarDto( "Morris", 2, "1980", new PersonDto( "Bob" ) );
//when
Car car = NativeCarMapper.INSTANCE.carDtoToCar( carDto );
@ -105,7 +139,7 @@ public class NativeCarMapperTest extends MapperTestBase {
public void shouldApplyConverter() {
//given
Car car = new Car( "Morris", 2, 1980 );
Car car = new Car( "Morris", 2, 1980, new Person( "Bob" ) );
//when
CarDto carDto = NativeCarMapper.INSTANCE.carToCarDto( car );
@ -119,7 +153,7 @@ public class NativeCarMapperTest extends MapperTestBase {
public void shouldApplyConverterForReverseMapping() {
//given
CarDto carDto = new CarDto( "Morris", 2, "1980" );
CarDto carDto = new CarDto( "Morris", 2, "1980", new PersonDto( "Bob" ) );
//when
Car car = NativeCarMapper.INSTANCE.carDtoToCar( carDto );

View File

@ -18,18 +18,18 @@ package de.moapa.maple.ap.test.model;
public class Car {
private String make;
private int numberOfSeats;
private int yearOfManufacture;
private Person driver;
public Car() {
}
public Car(String make, int numberOfSeats, int yearOfManufacture) {
public Car(String make, int numberOfSeats, int yearOfManufacture, Person driver) {
this.make = make;
this.numberOfSeats = numberOfSeats;
this.yearOfManufacture = yearOfManufacture;
this.driver = driver;
}
public String getMake() {
@ -55,4 +55,12 @@ public class Car {
public void setYearOfManufacture(int yearOfManufacture) {
this.yearOfManufacture = yearOfManufacture;
}
public Person getDriver() {
return driver;
}
public void setDriver(Person driver) {
this.driver = driver;
}
}

View File

@ -18,18 +18,18 @@ package de.moapa.maple.ap.test.model;
public class CarDto {
private String make;
private int seatCount;
private String manufacturingYear;
private PersonDto driver;
public CarDto() {
}
public CarDto(String make, int seatCount, String manufacturingYear) {
public CarDto(String make, int seatCount, String manufacturingYear, PersonDto driver) {
this.make = make;
this.seatCount = seatCount;
this.manufacturingYear = manufacturingYear;
this.driver = driver;
}
public String getMake() {
@ -55,4 +55,12 @@ public class CarDto {
public void setManufacturingYear(String manufacturingYear) {
this.manufacturingYear = manufacturingYear;
}
public PersonDto getDriver() {
return driver;
}
public void setDriver(PersonDto driver) {
this.driver = driver;
}
}

View File

@ -0,0 +1,36 @@
/**
* 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.test.model;
public class Person {
private String name;
public Person() {
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,36 @@
/**
* 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.test.model;
public class PersonDto {
private String name;
public PersonDto() {
}
public PersonDto(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}