#163 Adding support for decorators

This commit is contained in:
Gunnar Morling 2014-03-04 23:09:36 +01:00
parent 427cc16390
commit e0da882540
21 changed files with 904 additions and 5 deletions

View File

@ -0,0 +1,56 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Specifies a decorator to be applied to a generated mapper, which e.g. can be used to amend mappings performed by
* generated mapping methods.
* <p>
* A typical decorator implementation will be an abstract class and only implement/override a subset of the methods of
* the mapper type which it decorates. All methods not implemented or overridden by the decorator will be implemented by
* the code generator.
* <p>
* If a constructor with a single parameter accepting the type of the decorated mapper is present, a delegate with
* generated implementations of all the mapper methods will be passed to this constructor. A typical implementation will
* store the passed delegate in a field of the decorator and make use of it in the decorator methods.
* <p>
* <b>NOTE:</b> The decorator feature is considered experimental and it may change in future releases. Currently
* decorators are only supported for the default component model, not for the CDI and Spring models.
*
* @author Gunnar Morling
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DecoratedWith {
/**
* The decorator type. Must extend or implement the mapper type to which it is applied.
* <p>
* The decorator type must either have a default constructor or a constructor with a single parameter accepting the
* type of the decorated mapper.
*
* @return the decorator type
*/
Class<?> value();
}

View File

@ -0,0 +1,88 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.model;
import java.util.Arrays;
import java.util.List;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;
import org.mapstruct.ap.model.common.Accessibility;
import org.mapstruct.ap.model.common.ModelElement;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.model.common.TypeFactory;
import org.mapstruct.ap.prism.DecoratedWithPrism;
/**
* Represents a decorator applied to a generated mapper type.
*
* @author Gunnar Morling
*/
public class Decorator extends GeneratedType {
private static final String IMPLEMENTATION_SUFFIX = "Impl";
private Decorator(TypeFactory typeFactory, String packageName, String name, String superClassName,
String interfaceName, List<MappingMethod> methods, List<? extends ModelElement> fields,
boolean suppressGeneratorTimestamp, Accessibility accessibility) {
super(
typeFactory,
packageName,
name,
superClassName,
interfaceName,
methods,
fields,
suppressGeneratorTimestamp,
accessibility
);
}
public static Decorator getInstance(Elements elementUtils, TypeFactory typeFactory, TypeElement mapperElement,
DecoratedWithPrism decoratorPrism, List<MappingMethod> methods,
boolean hasDelegateConstructor,
boolean suppressGeneratorTimestamp) {
Type decoratorType = typeFactory.getType( decoratorPrism.value() );
return new Decorator(
typeFactory,
elementUtils.getPackageOf( mapperElement ).getQualifiedName().toString(),
mapperElement.getSimpleName().toString() + IMPLEMENTATION_SUFFIX,
decoratorType.getName(),
mapperElement.getKind() == ElementKind.INTERFACE ? mapperElement.getSimpleName().toString() : null,
methods,
Arrays.asList(
new Field( typeFactory.getType( mapperElement ), "delegate" ),
new DecoratorConstructor(
mapperElement.getSimpleName().toString() + IMPLEMENTATION_SUFFIX,
mapperElement.getSimpleName().toString() + "Impl_",
hasDelegateConstructor
)
),
suppressGeneratorTimestamp,
Accessibility.fromModifiers( mapperElement.getModifiers() )
);
}
@Override
protected String getTemplateName() {
return GeneratedType.class.getName() + ".ftl";
}
}

View File

@ -0,0 +1,60 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.model;
import java.util.Collections;
import java.util.Set;
import org.mapstruct.ap.model.common.ModelElement;
import org.mapstruct.ap.model.common.Type;
/**
* Represents the constructor of a decorator.
*
* @author Gunnar Morling
*/
public class DecoratorConstructor extends ModelElement {
private final String name;
private final String delegateName;
private final boolean invokeSuperConstructor;
public DecoratorConstructor(String name, String delegateName, boolean invokeSuperConstructor) {
this.name = name;
this.delegateName = delegateName;
this.invokeSuperConstructor = invokeSuperConstructor;
}
@Override
public Set<Type> getImportTypes() {
return Collections.emptySet();
}
public String getName() {
return name;
}
public String getDelegateName() {
return delegateName;
}
public boolean isInvokeSuperConstructor() {
return invokeSuperConstructor;
}
}

View File

@ -0,0 +1,33 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.model;
import org.mapstruct.ap.model.source.Method;
/**
* A method of a decorator which delegates to the corresponding method of the generated mapper implementation.
*
* @author Gunnar Morling
*/
public class DelegatingMethod extends MappingMethod {
public DelegatingMethod(Method method) {
super( method );
}
}

View File

@ -35,13 +35,15 @@ import org.mapstruct.ap.model.common.TypeFactory;
public class Mapper extends GeneratedType { public class Mapper extends GeneratedType {
private static final String IMPLEMENTATION_SUFFIX = "Impl"; private static final String IMPLEMENTATION_SUFFIX = "Impl";
private static final String DECORATED_IMPLEMENTATION_SUFFIX = "Impl_";
private final List<MapperReference> referencedMappers; private final List<MapperReference> referencedMappers;
private final Decorator decorator;
private Mapper(TypeFactory typeFactory, String packageName, String name, String superClassName, private Mapper(TypeFactory typeFactory, String packageName, String name, String superClassName,
String interfaceName, String interfaceName,
List<MappingMethod> methods, boolean suppressGeneratorTimestamp, Accessibility accessibility, List<MappingMethod> methods, boolean suppressGeneratorTimestamp, Accessibility accessibility,
List<MapperReference> referencedMappers) { List<MapperReference> referencedMappers, Decorator decorator) {
super( super(
typeFactory, typeFactory,
@ -56,6 +58,7 @@ public class Mapper extends GeneratedType {
); );
this.referencedMappers = referencedMappers; this.referencedMappers = referencedMappers;
this.decorator = decorator;
} }
public static class Builder { public static class Builder {
@ -67,6 +70,7 @@ public class Mapper extends GeneratedType {
private Elements elementUtils; private Elements elementUtils;
private boolean suppressGeneratorTimestamp; private boolean suppressGeneratorTimestamp;
private Decorator decorator;
public Builder element(TypeElement element) { public Builder element(TypeElement element) {
this.element = element; this.element = element;
@ -98,8 +102,14 @@ public class Mapper extends GeneratedType {
return this; return this;
} }
public Builder decorator(Decorator decorator) {
this.decorator = decorator;
return this;
}
public Mapper build() { public Mapper build() {
String implementationName = element.getSimpleName() + IMPLEMENTATION_SUFFIX; String implementationName = element.getSimpleName()
+ ( decorator == null ? IMPLEMENTATION_SUFFIX : DECORATED_IMPLEMENTATION_SUFFIX );
return new Mapper( return new Mapper(
typeFactory, typeFactory,
@ -110,7 +120,8 @@ public class Mapper extends GeneratedType {
mappingMethods, mappingMethods,
suppressGeneratorTimestamp, suppressGeneratorTimestamp,
Accessibility.fromModifiers( element.getModifiers() ), Accessibility.fromModifiers( element.getModifiers() ),
mapperReferences mapperReferences,
decorator
); );
} }
} }
@ -119,6 +130,10 @@ public class Mapper extends GeneratedType {
return referencedMappers; return referencedMappers;
} }
public Decorator getDecorator() {
return decorator;
}
@Override @Override
protected String getTemplateName() { protected String getTemplateName() {
return GeneratedType.class.getName() + ".ftl"; return GeneratedType.class.getName() + ".ftl";

View File

@ -19,16 +19,17 @@
package org.mapstruct.ap.prism; package org.mapstruct.ap.prism;
import javax.xml.bind.annotation.XmlElementDecl; import javax.xml.bind.annotation.XmlElementDecl;
import net.java.dev.hickory.prism.GeneratePrism; import net.java.dev.hickory.prism.GeneratePrism;
import net.java.dev.hickory.prism.GeneratePrisms; import net.java.dev.hickory.prism.GeneratePrisms;
import org.mapstruct.DecoratedWith;
import org.mapstruct.IterableMapping; import org.mapstruct.IterableMapping;
import org.mapstruct.MapMapping; import org.mapstruct.MapMapping;
import org.mapstruct.Mapper; import org.mapstruct.Mapper;
import org.mapstruct.Mapping; import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget; import org.mapstruct.MappingTarget;
import org.mapstruct.TargetType;
import org.mapstruct.Mappings; import org.mapstruct.Mappings;
import org.mapstruct.TargetType;
/** /**
* Triggers the generation of prism types using <a href="https://java.net/projects/hickory">Hickory</a>. * Triggers the generation of prism types using <a href="https://java.net/projects/hickory">Hickory</a>.
@ -43,6 +44,7 @@ import org.mapstruct.Mappings;
@GeneratePrism(value = MapMapping.class, publicAccess = true), @GeneratePrism(value = MapMapping.class, publicAccess = true),
@GeneratePrism(value = TargetType.class, publicAccess = true), @GeneratePrism(value = TargetType.class, publicAccess = true),
@GeneratePrism(value = MappingTarget.class, publicAccess = true), @GeneratePrism(value = MappingTarget.class, publicAccess = true),
@GeneratePrism(value = DecoratedWith.class, publicAccess = true),
// external types // external types
@GeneratePrism(value = XmlElementDecl.class, publicAccess = true) @GeneratePrism(value = XmlElementDecl.class, publicAccess = true)

View File

@ -32,6 +32,7 @@ import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement; import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.ElementFilter;
import javax.lang.model.util.Elements; import javax.lang.model.util.Elements;
import javax.lang.model.util.Types; import javax.lang.model.util.Types;
import javax.tools.Diagnostic.Kind; import javax.tools.Diagnostic.Kind;
@ -39,7 +40,9 @@ import javax.tools.Diagnostic.Kind;
import org.mapstruct.ap.conversion.ConversionProvider; import org.mapstruct.ap.conversion.ConversionProvider;
import org.mapstruct.ap.conversion.Conversions; import org.mapstruct.ap.conversion.Conversions;
import org.mapstruct.ap.model.BeanMappingMethod; import org.mapstruct.ap.model.BeanMappingMethod;
import org.mapstruct.ap.model.Decorator;
import org.mapstruct.ap.model.DefaultMapperReference; import org.mapstruct.ap.model.DefaultMapperReference;
import org.mapstruct.ap.model.DelegatingMethod;
import org.mapstruct.ap.model.EnumMappingMethod; import org.mapstruct.ap.model.EnumMappingMethod;
import org.mapstruct.ap.model.IterableMappingMethod; import org.mapstruct.ap.model.IterableMappingMethod;
import org.mapstruct.ap.model.MapMappingMethod; import org.mapstruct.ap.model.MapMappingMethod;
@ -64,6 +67,7 @@ import org.mapstruct.ap.model.source.builtin.BuiltInMethod;
import org.mapstruct.ap.model.source.selector.MethodSelectors; import org.mapstruct.ap.model.source.selector.MethodSelectors;
import org.mapstruct.ap.option.Options; import org.mapstruct.ap.option.Options;
import org.mapstruct.ap.option.ReportingPolicy; import org.mapstruct.ap.option.ReportingPolicy;
import org.mapstruct.ap.prism.DecoratedWithPrism;
import org.mapstruct.ap.prism.MapperPrism; import org.mapstruct.ap.prism.MapperPrism;
import org.mapstruct.ap.util.Executables; import org.mapstruct.ap.util.Executables;
import org.mapstruct.ap.util.Filters; import org.mapstruct.ap.util.Filters;
@ -127,6 +131,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
.mappingMethods( mappingMethods ) .mappingMethods( mappingMethods )
.mapperReferences( mapperReferences ) .mapperReferences( mapperReferences )
.suppressGeneratorTimestamp( options.isSuppressGeneratorTimestamp() ) .suppressGeneratorTimestamp( options.isSuppressGeneratorTimestamp() )
.decorator( getDecorator( element, methods ) )
.typeFactory( typeFactory ) .typeFactory( typeFactory )
.elementUtils( elementUtils ) .elementUtils( elementUtils )
.build(); .build();
@ -159,6 +164,70 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
} }
} }
private Decorator getDecorator(TypeElement element, List<SourceMethod> methods) {
DecoratedWithPrism decoratorPrism = DecoratedWithPrism.getInstanceOn( element );
if ( decoratorPrism == null ) {
return null;
}
TypeElement decoratorElement = (TypeElement) typeUtils.asElement( decoratorPrism.value() );
List<MappingMethod> mappingMethods = new ArrayList<MappingMethod>( methods.size() );
for ( SourceMethod mappingMethod : methods ) {
boolean implementationRequired = true;
for ( ExecutableElement method : ElementFilter.methodsIn( decoratorElement.getEnclosedElements() ) ) {
if ( elementUtils.overrides( method, mappingMethod.getExecutable(), decoratorElement ) ) {
implementationRequired = false;
break;
}
}
if ( implementationRequired ) {
mappingMethods.add( new DelegatingMethod( mappingMethod ) );
}
}
boolean hasDelegateConstructor = false;
boolean hasDefaultConstructor = false;
for ( ExecutableElement constructor : ElementFilter.constructorsIn( decoratorElement.getEnclosedElements() ) ) {
if ( constructor.getParameters().isEmpty() ) {
hasDefaultConstructor = true;
}
else if ( constructor.getParameters().size() == 1 ) {
if ( typeUtils.isAssignable(
element.asType(),
constructor.getParameters().iterator().next().asType()
) ) {
hasDelegateConstructor = true;
}
}
}
if ( !hasDelegateConstructor && !hasDefaultConstructor ) {
messager.printMessage(
Kind.ERROR,
String.format(
"Specified decorator type has no default constructor nor a constructor with a single " +
"parameter accepting the decorated mapper type."
),
element,
decoratorPrism.mirror
);
}
return Decorator.getInstance(
elementUtils,
typeFactory,
element,
decoratorPrism,
mappingMethods,
hasDelegateConstructor,
options.isSuppressGeneratorTimestamp()
);
}
private List<MapperReference> getReferencedMappers(TypeElement element) { private List<MapperReference> getReferencedMappers(TypeElement element) {
List<MapperReference> mapperReferences = new LinkedList<MapperReference>(); List<MapperReference> mapperReferences = new LinkedList<MapperReference>();
List<String> variableNames = new LinkedList<String>(); List<String> variableNames = new LinkedList<String>();

View File

@ -48,6 +48,10 @@ public class MapperRenderingProcessor implements ModelElementProcessor<Mapper, V
ModelWriter modelWriter = new ModelWriter(); ModelWriter modelWriter = new ModelWriter();
createSourceFile( model, modelWriter, filer ); createSourceFile( model, modelWriter, filer );
if ( model.getDecorator() != null ) {
createSourceFile( model.getDecorator(), modelWriter, filer );
}
} }
private void createSourceFile(GeneratedType model, ModelWriter modelWriter, Filer filer) { private void createSourceFile(GeneratedType model, ModelWriter modelWriter, Filer filer) {

View File

@ -0,0 +1,26 @@
<#--
Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
and/or other contributors as indicated by the @authors tag. See the
copyright.txt file in the distribution for a full listing of all
contributors.
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.
-->
public ${name}() {
<#if invokeSuperConstructor>
super( new ${delegateName}() );
</#if>
this.delegate = new ${delegateName}();
}

View File

@ -0,0 +1,24 @@
<#--
Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
and/or other contributors as indicated by the @authors tag. See the
copyright.txt file in the distribution for a full listing of all
contributors.
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.
-->
@Override
public <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, </#if></#list>) {
return delegate.${name}( <#list parameters as param>${param.name}<#if param_has_next>, </#if></#list> );
}

View File

@ -0,0 +1,36 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.test.decorator;
public class Address {
private String addressLine;
public Address(String addressLine) {
this.addressLine = addressLine;
}
public String getAddressLine() {
return addressLine;
}
public void setAddressLine(String addressLine) {
this.addressLine = addressLine;
}
}

View File

@ -0,0 +1,32 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.test.decorator;
public class AddressDto {
private String addressLine;
public String getAddressLine() {
return addressLine;
}
public void setAddressLine(String addressLine) {
this.addressLine = addressLine;
}
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.test.decorator;
import org.mapstruct.DecoratedWith;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.factory.Mappers;
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
@DecoratedWith(AnotherPersonMapperDecorator.class)
public interface AnotherPersonMapper {
AnotherPersonMapper INSTANCE = Mappers.getMapper( AnotherPersonMapper.class );
PersonDto personToPersonDto(Person person);
AddressDto addressToAddressDto(Address address);
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.test.decorator;
public abstract class AnotherPersonMapperDecorator implements AnotherPersonMapper {
@Override
public PersonDto personToPersonDto(Person person) {
AddressDto addressDto = new AddressDto();
addressDto.setAddressLine( person.getAddress().getAddressLine() );
PersonDto dto = new PersonDto();
dto.setDateOfBirth( person.getDateOfBirth() );
dto.setName( person.getFirstName() + " " + person.getLastName() );
dto.setAddress( addressDto );
return dto;
}
}

View File

@ -0,0 +1,123 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.test.decorator;
import java.util.Calendar;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.MapperTestBase;
import org.mapstruct.ap.testutil.WithClasses;
import org.testng.annotations.Test;
import static org.fest.assertions.Assertions.assertThat;
/**
* Test for the application of decorators.
*
* @author Gunnar Morling
*/
@WithClasses({
Person.class,
PersonDto.class,
Address.class,
AddressDto.class
})
@IssueKey("163")
public class DecoratorTest extends MapperTestBase {
@Test
@WithClasses({
PersonMapper.class,
PersonMapperDecorator.class
})
public void shouldInvokeDecoratorMethods() {
//given
Calendar birthday = Calendar.getInstance();
birthday.set( 1928, 4, 23 );
Person person = new Person( "Gary", "Crant", birthday.getTime(), new Address( "42 Ocean View Drive" ) );
//when
PersonDto personDto = PersonMapper.INSTANCE.personToPersonDto( person );
//then
assertThat( personDto ).isNotNull();
assertThat( personDto.getName() ).isEqualTo( "Gary Crant" );
assertThat( personDto.getAddress() ).isNotNull();
assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "42 Ocean View Drive" );
}
@Test
@WithClasses({
PersonMapper.class,
PersonMapperDecorator.class
})
public void shouldDelegateNonDecoratedMethodsToDefaultImplementation() {
//given
Address address = new Address( "42 Ocean View Drive" );
//when
AddressDto addressDto = PersonMapper.INSTANCE.addressToAddressDto( address );
//then
assertThat( addressDto ).isNotNull();
assertThat( addressDto.getAddressLine() ).isEqualTo( "42 Ocean View Drive" );
}
@Test
@WithClasses({
AnotherPersonMapper.class,
AnotherPersonMapperDecorator.class
})
public void shouldApplyDecoratorWithDefaultConstructor() {
//given
Calendar birthday = Calendar.getInstance();
birthday.set( 1928, 4, 23 );
Person person = new Person( "Gary", "Crant", birthday.getTime(), new Address( "42 Ocean View Drive" ) );
//when
PersonDto personDto = AnotherPersonMapper.INSTANCE.personToPersonDto( person );
//then
assertThat( personDto ).isNotNull();
assertThat( personDto.getName() ).isEqualTo( "Gary Crant" );
assertThat( personDto.getAddress() ).isNotNull();
assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "42 Ocean View Drive" );
}
@Test
@WithClasses({
YetAnotherPersonMapper.class,
YetAnotherPersonMapperDecorator.class
})
public void shouldApplyDelegateToClassBasedMapper() {
//given
Calendar birthday = Calendar.getInstance();
birthday.set( 1928, 4, 23 );
Person person = new Person( "Gary", "Crant", birthday.getTime(), new Address( "42 Ocean View Drive" ) );
//when
PersonDto personDto = YetAnotherPersonMapper.INSTANCE.personToPersonDto( person );
//then
assertThat( personDto ).isNotNull();
assertThat( personDto.getName() ).isEqualTo( "Gary Crant" );
assertThat( personDto.getAddress() ).isNotNull();
assertThat( personDto.getAddress().getAddressLine() ).isEqualTo( "42 Ocean View Drive" );
}
}

View File

@ -0,0 +1,68 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.test.decorator;
import java.util.Date;
public class Person {
private String firstName;
private String lastName;
private Date dateOfBirth;
private Address address;
public Person(String firstName, String lastName, Date dateOfBirth, Address address) {
this.firstName = firstName;
this.lastName = lastName;
this.dateOfBirth = dateOfBirth;
this.address = address;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Date getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}

View File

@ -0,0 +1,52 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.test.decorator;
import java.util.Date;
public class PersonDto {
private String name;
private Date dateOfBirth;
private AddressDto address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getDateOfBirth() {
return dateOfBirth;
}
public void setDateOfBirth(Date dateOfBirth) {
this.dateOfBirth = dateOfBirth;
}
public AddressDto getAddress() {
return address;
}
public void setAddress(AddressDto address) {
this.address = address;
}
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.test.decorator;
import org.mapstruct.DecoratedWith;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.factory.Mappers;
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
@DecoratedWith(PersonMapperDecorator.class)
public interface PersonMapper {
PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class );
PersonDto personToPersonDto(Person person);
AddressDto addressToAddressDto(Address address);
}

View File

@ -0,0 +1,36 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.test.decorator;
public abstract class PersonMapperDecorator implements PersonMapper {
private final PersonMapper delegate;
public PersonMapperDecorator(PersonMapper delegate) {
this.delegate = delegate;
}
@Override
public PersonDto personToPersonDto(Person person) {
PersonDto dto = delegate.personToPersonDto( person );
dto.setName( person.getFirstName() + " " + person.getLastName() );
return dto;
}
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.test.decorator;
import org.mapstruct.DecoratedWith;
import org.mapstruct.Mapper;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.factory.Mappers;
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
@DecoratedWith(YetAnotherPersonMapperDecorator.class)
public abstract class YetAnotherPersonMapper {
public static final YetAnotherPersonMapper INSTANCE = Mappers.getMapper( YetAnotherPersonMapper.class );
public abstract PersonDto personToPersonDto(Person person);
public abstract AddressDto addressToAddressDto(Address address);
}

View File

@ -0,0 +1,35 @@
/**
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.test.decorator;
public abstract class YetAnotherPersonMapperDecorator extends YetAnotherPersonMapper {
@Override
public PersonDto personToPersonDto(Person person) {
AddressDto addressDto = new AddressDto();
addressDto.setAddressLine( person.getAddress().getAddressLine() );
PersonDto dto = new PersonDto();
dto.setDateOfBirth( person.getDateOfBirth() );
dto.setName( person.getFirstName() + " " + person.getLastName() );
dto.setAddress( addressDto );
return dto;
}
}