diff --git a/core/src/main/java/org/mapstruct/DecoratedWith.java b/core/src/main/java/org/mapstruct/DecoratedWith.java new file mode 100644 index 000000000..215843ec0 --- /dev/null +++ b/core/src/main/java/org/mapstruct/DecoratedWith.java @@ -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. + *

+ * 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. + *

+ * 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. + *

+ * NOTE: 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. + *

+ * 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(); +} diff --git a/processor/src/main/java/org/mapstruct/ap/model/Decorator.java b/processor/src/main/java/org/mapstruct/ap/model/Decorator.java new file mode 100644 index 000000000..57f43ab5c --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/model/Decorator.java @@ -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 methods, List 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 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"; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/model/DecoratorConstructor.java b/processor/src/main/java/org/mapstruct/ap/model/DecoratorConstructor.java new file mode 100644 index 000000000..ee0f5a636 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/model/DecoratorConstructor.java @@ -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 getImportTypes() { + return Collections.emptySet(); + } + + public String getName() { + return name; + } + + public String getDelegateName() { + return delegateName; + } + + public boolean isInvokeSuperConstructor() { + return invokeSuperConstructor; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/model/DelegatingMethod.java b/processor/src/main/java/org/mapstruct/ap/model/DelegatingMethod.java new file mode 100644 index 000000000..79216b952 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/model/DelegatingMethod.java @@ -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 ); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/model/Mapper.java b/processor/src/main/java/org/mapstruct/ap/model/Mapper.java index 4e23aa7fc..9f7e95f87 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/Mapper.java +++ b/processor/src/main/java/org/mapstruct/ap/model/Mapper.java @@ -35,13 +35,15 @@ import org.mapstruct.ap.model.common.TypeFactory; public class Mapper extends GeneratedType { private static final String IMPLEMENTATION_SUFFIX = "Impl"; + private static final String DECORATED_IMPLEMENTATION_SUFFIX = "Impl_"; private final List referencedMappers; + private final Decorator decorator; private Mapper(TypeFactory typeFactory, String packageName, String name, String superClassName, String interfaceName, List methods, boolean suppressGeneratorTimestamp, Accessibility accessibility, - List referencedMappers) { + List referencedMappers, Decorator decorator) { super( typeFactory, @@ -56,6 +58,7 @@ public class Mapper extends GeneratedType { ); this.referencedMappers = referencedMappers; + this.decorator = decorator; } public static class Builder { @@ -67,6 +70,7 @@ public class Mapper extends GeneratedType { private Elements elementUtils; private boolean suppressGeneratorTimestamp; + private Decorator decorator; public Builder element(TypeElement element) { this.element = element; @@ -98,8 +102,14 @@ public class Mapper extends GeneratedType { return this; } + public Builder decorator(Decorator decorator) { + this.decorator = decorator; + return this; + } + public Mapper build() { - String implementationName = element.getSimpleName() + IMPLEMENTATION_SUFFIX; + String implementationName = element.getSimpleName() + + ( decorator == null ? IMPLEMENTATION_SUFFIX : DECORATED_IMPLEMENTATION_SUFFIX ); return new Mapper( typeFactory, @@ -110,7 +120,8 @@ public class Mapper extends GeneratedType { mappingMethods, suppressGeneratorTimestamp, Accessibility.fromModifiers( element.getModifiers() ), - mapperReferences + mapperReferences, + decorator ); } } @@ -119,6 +130,10 @@ public class Mapper extends GeneratedType { return referencedMappers; } + public Decorator getDecorator() { + return decorator; + } + @Override protected String getTemplateName() { return GeneratedType.class.getName() + ".ftl"; diff --git a/processor/src/main/java/org/mapstruct/ap/prism/PrismGenerator.java b/processor/src/main/java/org/mapstruct/ap/prism/PrismGenerator.java index 5e72094ee..27864e56e 100644 --- a/processor/src/main/java/org/mapstruct/ap/prism/PrismGenerator.java +++ b/processor/src/main/java/org/mapstruct/ap/prism/PrismGenerator.java @@ -19,16 +19,17 @@ package org.mapstruct.ap.prism; import javax.xml.bind.annotation.XmlElementDecl; + import net.java.dev.hickory.prism.GeneratePrism; import net.java.dev.hickory.prism.GeneratePrisms; - +import org.mapstruct.DecoratedWith; import org.mapstruct.IterableMapping; import org.mapstruct.MapMapping; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.MappingTarget; -import org.mapstruct.TargetType; import org.mapstruct.Mappings; +import org.mapstruct.TargetType; /** * Triggers the generation of prism types using Hickory. @@ -43,6 +44,7 @@ import org.mapstruct.Mappings; @GeneratePrism(value = MapMapping.class, publicAccess = true), @GeneratePrism(value = TargetType.class, publicAccess = true), @GeneratePrism(value = MappingTarget.class, publicAccess = true), + @GeneratePrism(value = DecoratedWith.class, publicAccess = true), // external types @GeneratePrism(value = XmlElementDecl.class, publicAccess = true) diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java index 52cfacf9a..8fdf845c8 100644 --- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java @@ -32,6 +32,7 @@ import javax.lang.model.element.ExecutableElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementFilter; import javax.lang.model.util.Elements; import javax.lang.model.util.Types; 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.Conversions; import org.mapstruct.ap.model.BeanMappingMethod; +import org.mapstruct.ap.model.Decorator; import org.mapstruct.ap.model.DefaultMapperReference; +import org.mapstruct.ap.model.DelegatingMethod; import org.mapstruct.ap.model.EnumMappingMethod; import org.mapstruct.ap.model.IterableMappingMethod; 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.option.Options; import org.mapstruct.ap.option.ReportingPolicy; +import org.mapstruct.ap.prism.DecoratedWithPrism; import org.mapstruct.ap.prism.MapperPrism; import org.mapstruct.ap.util.Executables; import org.mapstruct.ap.util.Filters; @@ -127,6 +131,7 @@ public class MapperCreationProcessor implements ModelElementProcessor methods) { + DecoratedWithPrism decoratorPrism = DecoratedWithPrism.getInstanceOn( element ); + + if ( decoratorPrism == null ) { + return null; + } + + TypeElement decoratorElement = (TypeElement) typeUtils.asElement( decoratorPrism.value() ); + + List mappingMethods = new ArrayList( 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 getReferencedMappers(TypeElement element) { List mapperReferences = new LinkedList(); List variableNames = new LinkedList(); diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MapperRenderingProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MapperRenderingProcessor.java index 4ff3ab996..dce45699b 100644 --- a/processor/src/main/java/org/mapstruct/ap/processor/MapperRenderingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperRenderingProcessor.java @@ -48,6 +48,10 @@ public class MapperRenderingProcessor implements ModelElementProcessor +public ${name}() { + <#if invokeSuperConstructor> + super( new ${delegateName}() ); + + this.delegate = new ${delegateName}(); +} \ No newline at end of file diff --git a/processor/src/main/resources/org.mapstruct.ap.model.DelegatingMethod.ftl b/processor/src/main/resources/org.mapstruct.ap.model.DelegatingMethod.ftl new file mode 100644 index 000000000..af61c459a --- /dev/null +++ b/processor/src/main/resources/org.mapstruct.ap.model.DelegatingMethod.ftl @@ -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>, ) { + return delegate.${name}( <#list parameters as param>${param.name}<#if param_has_next>, ); +} \ No newline at end of file diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/Address.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/Address.java new file mode 100644 index 000000000..55a125c78 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/Address.java @@ -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; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/AddressDto.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/AddressDto.java new file mode 100644 index 000000000..e6b83bb96 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/AddressDto.java @@ -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; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/AnotherPersonMapper.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/AnotherPersonMapper.java new file mode 100644 index 000000000..39f2ba069 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/AnotherPersonMapper.java @@ -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); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/AnotherPersonMapperDecorator.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/AnotherPersonMapperDecorator.java new file mode 100644 index 000000000..c2cf95434 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/AnotherPersonMapperDecorator.java @@ -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; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratorTest.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratorTest.java new file mode 100644 index 000000000..a425a0939 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/DecoratorTest.java @@ -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" ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/Person.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/Person.java new file mode 100644 index 000000000..c69972e24 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/Person.java @@ -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; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/PersonDto.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/PersonDto.java new file mode 100644 index 000000000..f59721a3e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/PersonDto.java @@ -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; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/PersonMapper.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/PersonMapper.java new file mode 100644 index 000000000..415a0773c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/PersonMapper.java @@ -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); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/PersonMapperDecorator.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/PersonMapperDecorator.java new file mode 100644 index 000000000..6ae197aae --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/PersonMapperDecorator.java @@ -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; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/YetAnotherPersonMapper.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/YetAnotherPersonMapper.java new file mode 100644 index 000000000..5a09fd89c --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/YetAnotherPersonMapper.java @@ -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); +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/decorator/YetAnotherPersonMapperDecorator.java b/processor/src/test/java/org/mapstruct/ap/test/decorator/YetAnotherPersonMapperDecorator.java new file mode 100644 index 000000000..2a02434f3 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/decorator/YetAnotherPersonMapperDecorator.java @@ -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; + } +}