#592 Fix Decorator-support for component model 'spring' and 'jsr330', extend decorator integration-tests, add examples to @DecoratedWith javadoc

This commit is contained in:
Andreas Gudian 2015-07-07 11:35:14 +02:00
parent 4eaacbcfe6
commit 590363cf2f
28 changed files with 959 additions and 60 deletions

View File

@ -29,14 +29,131 @@ import java.lang.annotation.Target;
* <p> * <p>
* A typical decorator implementation will be an abstract class and only implement/override a subset of the methods of * 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 mapper type which it decorates. All methods not implemented or overridden by the decorator will be implemented by
* the code generator. * the code generator by delegating to the generated mapper implementation.
* <p>
* <b>NOTE:</b> The usage of decorated mappers differs depending on the selected component model.
* <p>
* <b>NOTE:</b> This annotation is not supported for the component model {@code cdi}. Use CDI's own
* <a href="https://docs.jboss.org/cdi/spec/1.0/html/decorators.html">{@code @Decorator}</a> feature instead.
* <p>
* <b>NOTE:</b> The decorator feature when used with component model {@code jsr330} is considered <em>experimental</em>
* and it may change in future releases.
* <p>
* <h2>Examples</h2>
* <p>
* For the examples below, consider the following mapper declaration:
*
* <pre>
* &#64;Mapper(componentModel = "...")
* &#64;DecoratedWith(PersonMapperDecorator.class)
* public interface PersonMapper {
*
* &#64;Mapping(target = "name", ignore = true)
* PersonDto personToPersonDto(Person person);
*
* AddressDto addressToAddressDto(Address address); // not touched by the decorator
* }
* </pre>
*
* <h3>1. Component model 'default'</h3>
* <h4>Referencing the original mapper in the decorator</h4>
* <p> * <p>
* If a constructor with a single parameter accepting the type of the decorated mapper is present, a delegate with * 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 * 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. * store the passed delegate in a field of the decorator and make use of it in the decorator methods:
*
* <pre>
* public abstract class PersonMapperDecorator implements PersonMapper {
*
* private PersonMapper delegate;
*
* public PersonMapperDecorator(PersonMapper delegate) {
* this.delegate = delegate;
* }
*
* &#64;Override
* public PersonDto personToPersonDto(Person person) {
* PersonDto dto = delegate.personToPersonDto( person );
* dto.setName( person.getFirstName() + " " + person.getLastName() );
*
* return dto;
* }
* }
* </pre>
*
* <h4>Using the decorated mapper</h4>
* <p>
* Nothing special needs to be done. When using {@code Mappers.getMapper( PersonMapper.class )}, the <em>decorator</em>
* is returned, with the injected original mapper.
* <h3>2. Component model 'spring'</h3>
* <h4>Referencing the original mapper in the decorator</h4>
* <p>
* The generated implementation of the original mapper is annotated with the Spring's {@code @Qualifier("delegate")}. To
* autowire that bean in your decorator, add that qualifier annotation as well:
*
* <pre>
* public abstract class PersonMapperDecorator implements PersonMapper {
*
* &#64;Autowired
* &#64;Qualifier("delegate")
* private PersonMapper delegate;
*
* &#64;Override
* public PersonDto personToPersonDto(Person person) {
* PersonDto dto = delegate.personToPersonDto( person );
* dto.setName( person.getFirstName() + " " + person.getLastName() );
*
* return dto;
* }
* }
* </pre>
*
* <h4>Using the decorated mapper in the decorator</h4>
* <p>
* The generated class that extends the decorator is annotated with Spring's {@code @Primary} annotation. To autowire
* the decorated mapper in the application, nothing special needs to be done:
*
* <pre>
* &#64;Autowired
* private PersonMapper personMapper; // injects the decorator, with the injected original mapper
* </pre>
*
* <h3>3. Component model 'jsr330'</h3>
* <h4>Referencing the original mapper</h4>
* <p>
* JSR 330 doesn't specify qualifiers and only allows to specifically name the beans. Hence, the generated
* implementation of the original mapper is annotated with the {@code @Named("fully-qualified-name-of-generated-impl")}.
* To inject that bean in your decorator, add the same annotation to the delegate field:
*
* <pre>
* public abstract class PersonMapperDecorator implements PersonMapper {
*
* &#64;Inject
* &#64;Named("org.examples.PersonMapperImpl_")
* private PersonMapper delegate;
*
* &#64;Override
* public PersonDto personToPersonDto(Person person) {
* PersonDto dto = delegate.personToPersonDto( person );
* dto.setName( person.getFirstName() + " " + person.getLastName() );
*
* return dto;
* }
* }
* </pre>
*
* <h4>Using the decorated mapper in the decorator</h4>
* <p>
* Unlike with the other component models, the usage site must be aware if a mapper is decorated or not, as for
* decorated mappers, the parameterless {@code @Named} annotation must be added to select the <em>decorator</em> to be
* injected:
*
* <pre>
* &#64;Inject
* &#64;Named
* private PersonMapper personMapper; // injects the decorator, with the injected original mapper
* </pre>
* <p> * <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 * @author Gunnar Morling
*/ */
@ -45,10 +162,10 @@ import java.lang.annotation.Target;
public @interface DecoratedWith { public @interface DecoratedWith {
/** /**
* The decorator type. Must extend or implement the mapper type to which it is applied. * The decorator type. Must be an abstract class that extends or implements the mapper type to which it is applied.
* <p> * <p>
* The decorator type must either have a default constructor or a constructor with a single parameter accepting the * For component-model {@code default}, the decorator type must either have a default constructor or a constructor
* type of the decorated mapper. * with a single parameter accepting the type of the decorated mapper.
* *
* @return the decorator type * @return the decorator type
*/ */

View File

@ -26,4 +26,6 @@ import org.mapstruct.itest.cdi.other.DateMapper;
public interface DecoratedSourceTargetMapper { public interface DecoratedSourceTargetMapper {
Target sourceToTarget(Source source); Target sourceToTarget(Source source);
Target undecoratedSourceToTarget(Source source);
} }

View File

@ -23,15 +23,12 @@ import javax.decorator.Delegate;
import javax.inject.Inject; import javax.inject.Inject;
@Decorator @Decorator
public class SourceTargetMapperDecorator implements DecoratedSourceTargetMapper { public abstract class SourceTargetMapperDecorator implements DecoratedSourceTargetMapper {
@Delegate @Delegate
@Inject @Inject
private DecoratedSourceTargetMapper delegate; private DecoratedSourceTargetMapper delegate;
public SourceTargetMapperDecorator() {
}
@Override @Override
public Target sourceToTarget(Source source) { public Target sourceToTarget(Source source) {
Target t = delegate.sourceToTarget( source ); Target t = delegate.sourceToTarget( source );

View File

@ -0,0 +1,32 @@
/**
* Copyright 2012-2015 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.itest.jsr330;
import org.mapstruct.Mapper;
import org.mapstruct.DecoratedWith;
import org.mapstruct.itest.jsr330.other.DateMapper;
@Mapper(componentModel = "jsr330", uses = DateMapper.class)
@DecoratedWith(SourceTargetMapperDecorator.class)
public interface DecoratedSourceTargetMapper {
Target sourceToTarget(Source source);
Target undecoratedSourceToTarget(Source source);
}

View File

@ -0,0 +1,32 @@
/**
* Copyright 2012-2015 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.itest.jsr330;
import org.mapstruct.Mapper;
import org.mapstruct.DecoratedWith;
import org.mapstruct.itest.jsr330.other.DateMapper;
@Mapper(componentModel = "jsr330", uses = DateMapper.class)
@DecoratedWith(SecondSourceTargetMapperDecorator.class)
public interface SecondDecoratedSourceTargetMapper {
Target sourceToTarget(Source source);
Target undecoratedSourceToTarget(Source source);
}

View File

@ -0,0 +1,36 @@
/**
* Copyright 2012-2015 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.itest.jsr330;
import javax.inject.Inject;
import javax.inject.Named;
public abstract class SecondSourceTargetMapperDecorator implements SecondDecoratedSourceTargetMapper {
@Inject
@Named("org.mapstruct.itest.jsr330.SecondDecoratedSourceTargetMapperImpl_")
private SecondDecoratedSourceTargetMapper delegate;
@Override
public Target sourceToTarget(Source source) {
Target t = delegate.sourceToTarget( source );
t.setFoo( 43L );
return t;
}
}

View File

@ -0,0 +1,36 @@
/**
* Copyright 2012-2015 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.itest.jsr330;
import javax.inject.Inject;
import javax.inject.Named;
public abstract class SourceTargetMapperDecorator implements DecoratedSourceTargetMapper {
@Inject
@Named("org.mapstruct.itest.jsr330.DecoratedSourceTargetMapperImpl_")
private DecoratedSourceTargetMapper delegate;
@Override
public Target sourceToTarget(Source source) {
Target t = delegate.sourceToTarget( source );
t.setFoo( 43L );
return t;
}
}

View File

@ -18,9 +18,8 @@
*/ */
package org.mapstruct.itest.jsr330; package org.mapstruct.itest.jsr330;
import static org.fest.assertions.Assertions.assertThat;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Named;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@ -30,13 +29,15 @@ import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.fest.assertions.Assertions.assertThat;
/** /**
* Test for generation of JSR-330-based Mapper implementations * Test for generation of JSR-330-based Mapper implementations
* *
* @author Andreas Gudian * @author Andreas Gudian
*/ */
@ContextConfiguration(classes = SpringTestConfig.class ) @ContextConfiguration(classes = SpringTestConfig.class)
@RunWith( SpringJUnit4ClassRunner.class ) @RunWith(SpringJUnit4ClassRunner.class)
public class Jsr330BasedMapperTest { public class Jsr330BasedMapperTest {
@Configuration @Configuration
@ComponentScan(basePackageClasses = Jsr330BasedMapperTest.class) @ComponentScan(basePackageClasses = Jsr330BasedMapperTest.class)
@ -46,9 +47,16 @@ public class Jsr330BasedMapperTest {
@Inject @Inject
private SourceTargetMapper mapper; private SourceTargetMapper mapper;
@Inject
@Named
private DecoratedSourceTargetMapper decoratedMapper;
@Inject
@Named
private SecondDecoratedSourceTargetMapper secondDecoratedMapper;
@Test @Test
public void shouldCreateSpringBasedMapper() { public void shouldInjectJsr330BasedMapper() {
Source source = new Source(); Source source = new Source();
Target target = mapper.sourceToTarget( source ); Target target = mapper.sourceToTarget( source );
@ -57,4 +65,38 @@ public class Jsr330BasedMapperTest {
assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 42 ) ); assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 42 ) );
assertThat( target.getDate() ).isEqualTo( "1980" ); assertThat( target.getDate() ).isEqualTo( "1980" );
} }
@Test
public void shouldInjectDecorator() {
Source source = new Source();
Target target = decoratedMapper.sourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 43 ) );
assertThat( target.getDate() ).isEqualTo( "1980" );
target = decoratedMapper.undecoratedSourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 42 ) );
assertThat( target.getDate() ).isEqualTo( "1980" );
}
@Test
public void shouldInjectSecondDecorator() {
Source source = new Source();
Target target = secondDecoratedMapper.sourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 43 ) );
assertThat( target.getDate() ).isEqualTo( "1980" );
target = secondDecoratedMapper.undecoratedSourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 42 ) );
assertThat( target.getDate() ).isEqualTo( "1980" );
}
} }

View File

@ -27,4 +27,6 @@ import org.mapstruct.itest.spring.other.DateMapper;
public interface DecoratedSourceTargetMapper { public interface DecoratedSourceTargetMapper {
Target sourceToTarget(Source source); Target sourceToTarget(Source source);
Target undecoratedSourceToTarget(Source source);
} }

View File

@ -0,0 +1,32 @@
/**
* Copyright 2012-2015 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.itest.spring;
import org.mapstruct.Mapper;
import org.mapstruct.DecoratedWith;
import org.mapstruct.itest.spring.other.DateMapper;
@Mapper( componentModel = "spring", uses = DateMapper.class )
@DecoratedWith( SecondSourceTargetMapperDecorator.class )
public interface SecondDecoratedSourceTargetMapper {
Target sourceToTarget(Source source);
Target undecoratedSourceToTarget(Source source);
}

View File

@ -0,0 +1,36 @@
/**
* Copyright 2012-2015 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.itest.spring;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public abstract class SecondSourceTargetMapperDecorator implements SecondDecoratedSourceTargetMapper {
@Autowired
@Qualifier( "delegate" )
private DecoratedSourceTargetMapper delegate;
@Override
public Target sourceToTarget(Source source) {
Target t = delegate.sourceToTarget( source );
t.setFoo( 43L );
return t;
}
}

View File

@ -20,20 +20,13 @@ package org.mapstruct.itest.spring;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Component public abstract class SourceTargetMapperDecorator implements DecoratedSourceTargetMapper {
@Primary
public class SourceTargetMapperDecorator implements DecoratedSourceTargetMapper {
@Autowired @Autowired
@Qualifier( "delegate" ) @Qualifier( "delegate" )
private DecoratedSourceTargetMapper delegate; private DecoratedSourceTargetMapper delegate;
public SourceTargetMapperDecorator() {
}
@Override @Override
public Target sourceToTarget(Source source) { public Target sourceToTarget(Source source) {
Target t = delegate.sourceToTarget( source ); Target t = delegate.sourceToTarget( source );

View File

@ -18,25 +18,24 @@
*/ */
package org.mapstruct.itest.spring; package org.mapstruct.itest.spring;
import static org.fest.assertions.Assertions.assertThat;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mapstruct.itest.spring.SpringBasedMapperTest.SpringTestConfig; import org.mapstruct.itest.spring.SpringBasedMapperTest.SpringTestConfig;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.fest.assertions.Assertions.assertThat;
/** /**
* Test for generation of Spring-based Mapper implementations * Test for generation of Spring-based Mapper implementations
* *
* @author Andreas Gudian * @author Andreas Gudian
*/ */
@ContextConfiguration(classes = SpringTestConfig.class ) @ContextConfiguration(classes = SpringTestConfig.class)
@RunWith( SpringJUnit4ClassRunner.class ) @RunWith(SpringJUnit4ClassRunner.class)
public class SpringBasedMapperTest { public class SpringBasedMapperTest {
@Configuration @Configuration
@ -50,8 +49,11 @@ public class SpringBasedMapperTest {
@Autowired @Autowired
private DecoratedSourceTargetMapper decoratedMapper; private DecoratedSourceTargetMapper decoratedMapper;
@Autowired
private SecondDecoratedSourceTargetMapper secondDecoratedMapper;
@Test @Test
public void shouldCreateSpringBasedMapper() { public void shouldInjectSpringBasedMapper() {
Source source = new Source(); Source source = new Source();
Target target = mapper.sourceToTarget( source ); Target target = mapper.sourceToTarget( source );
@ -69,5 +71,29 @@ public class SpringBasedMapperTest {
assertThat( target ).isNotNull(); assertThat( target ).isNotNull();
assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 43 ) ); assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 43 ) );
assertThat( target.getDate() ).isEqualTo( "1980" );
target = decoratedMapper.undecoratedSourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 42 ) );
assertThat( target.getDate() ).isEqualTo( "1980" );
}
@Test
public void shouldInjectSecondDecorator() {
Source source = new Source();
Target target = secondDecoratedMapper.sourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 43 ) );
assertThat( target.getDate() ).isEqualTo( "1980" );
target = secondDecoratedMapper.undecoratedSourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getFoo() ).isEqualTo( Long.valueOf( 42 ) );
assertThat( target.getDate() ).isEqualTo( "1980" );
} }
} }

View File

@ -179,6 +179,12 @@
<version>${org.springframework.version}</version> <version>${org.springframework.version}</version>
</dependency> </dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<!-- Joda-Time --> <!-- Joda-Time -->
<dependency> <dependency>
<groupId>joda-time</groupId> <groupId>joda-time</groupId>

View File

@ -85,6 +85,23 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<scope>test</scope>
</dependency>
<!-- There is no compile dependency to Joda-Time; It's only required for testing the Joda conversions --> <!-- There is no compile dependency to Joda-Time; It's only required for testing the Joda conversions -->
<dependency> <dependency>
<groupId>joda-time</groupId> <groupId>joda-time</groupId>

View File

@ -18,10 +18,11 @@
*/ */
package org.mapstruct.ap.internal.model; package org.mapstruct.ap.internal.model;
import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import org.mapstruct.ap.internal.model.common.Type; import org.mapstruct.ap.internal.model.common.Type;
import org.mapstruct.ap.internal.util.Collections;
/** /**
* Mapper reference which is retrieved via Annotation-based dependency injection. * Mapper reference which is retrieved via Annotation-based dependency injection.
@ -31,19 +32,26 @@ import org.mapstruct.ap.internal.util.Collections;
*/ */
public class AnnotationMapperReference extends MapperReference { public class AnnotationMapperReference extends MapperReference {
private final Annotation annotation; private final List<Annotation> annotations;
public AnnotationMapperReference(Type type, String variableName, Annotation annotation, boolean isUsed) { public AnnotationMapperReference(Type type, String variableName, List<Annotation> annotations, boolean isUsed) {
super( type, variableName, isUsed ); super( type, variableName, isUsed );
this.annotation = annotation; this.annotations = annotations;
} }
public Annotation getAnnotation() { public List<Annotation> getAnnotations() {
return annotation; return annotations;
} }
@Override @Override
public Set<Type> getImportTypes() { public Set<Type> getImportTypes() {
return Collections.asSet( annotation.getImportTypes(), super.getType() ); Set<Type> types = new HashSet<Type>();
types.add( getType() );
for ( Annotation annotation : annotations ) {
types.addAll( annotation.getImportTypes() );
}
return types;
} }
} }

View File

@ -50,14 +50,14 @@ public abstract class GeneratedType extends ModelElement {
private final List<Annotation> annotations; private final List<Annotation> annotations;
private final List<MappingMethod> methods; private final List<MappingMethod> methods;
private final List<? extends Field> fields;
private final SortedSet<Type> extraImportedTypes; private final SortedSet<Type> extraImportedTypes;
private final boolean suppressGeneratorTimestamp; private final boolean suppressGeneratorTimestamp;
private final boolean suppressGeneratorVersionComment; private final boolean suppressGeneratorVersionComment;
private final VersionInformation versionInformation; private final VersionInformation versionInformation;
private final Accessibility accessibility; private final Accessibility accessibility;
private final Constructor constructor; private List<? extends Field> fields;
private Constructor constructor;
/** /**
* Type representing the {@code @Generated} annotation * Type representing the {@code @Generated} annotation
@ -128,10 +128,14 @@ public abstract class GeneratedType extends ModelElement {
return methods; return methods;
} }
public List<? extends ModelElement> getFields() { public List<? extends Field> getFields() {
return fields; return fields;
} }
public void setFields(List<? extends Field> fields) {
this.fields = fields;
}
public boolean isSuppressGeneratorTimestamp() { public boolean isSuppressGeneratorTimestamp() {
return suppressGeneratorTimestamp; return suppressGeneratorTimestamp;
} }
@ -182,6 +186,10 @@ public abstract class GeneratedType extends ModelElement {
return constructor; return constructor;
} }
public void removeConstructor() {
constructor = null;
}
protected void addWithDependents(Collection<Type> collection, Type typeToAdd) { protected void addWithDependents(Collection<Type> collection, Type typeToAdd) {
if ( typeToAdd == null ) { if ( typeToAdd == null ) {
return; return;

View File

@ -18,6 +18,8 @@
*/ */
package org.mapstruct.ap.internal.processor; package org.mapstruct.ap.internal.processor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
@ -25,6 +27,8 @@ import javax.lang.model.element.TypeElement;
import org.mapstruct.ap.internal.model.Annotation; import org.mapstruct.ap.internal.model.Annotation;
import org.mapstruct.ap.internal.model.AnnotationMapperReference; import org.mapstruct.ap.internal.model.AnnotationMapperReference;
import org.mapstruct.ap.internal.model.Decorator;
import org.mapstruct.ap.internal.model.Field;
import org.mapstruct.ap.internal.model.Mapper; import org.mapstruct.ap.internal.model.Mapper;
import org.mapstruct.ap.internal.model.MapperReference; import org.mapstruct.ap.internal.model.MapperReference;
import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.model.common.TypeFactory;
@ -64,27 +68,55 @@ public abstract class AnnotationBasedComponentModelProcessor implements ModelEle
if ( !requiresGenerationOfDecoratorClass() ) { if ( !requiresGenerationOfDecoratorClass() ) {
mapper.removeDecorator(); mapper.removeDecorator();
} }
else if ( mapper.getDecorator() != null ) {
adjustDecorator( mapper );
}
List<Annotation> annotations = getMapperReferenceAnnotations();
ListIterator<MapperReference> iterator = mapper.getReferencedMappers().listIterator(); ListIterator<MapperReference> iterator = mapper.getReferencedMappers().listIterator();
while ( iterator.hasNext() ) { while ( iterator.hasNext() ) {
MapperReference reference = iterator.next(); MapperReference reference = iterator.next();
iterator.remove(); iterator.remove();
iterator.add( replacementMapperReference( reference ) ); iterator.add( replacementMapperReference( reference, annotations ) );
} }
return mapper; return mapper;
} }
protected void adjustDecorator(Mapper mapper) {
Decorator decorator = mapper.getDecorator();
for ( Annotation typeAnnotation : getDecoratorAnnotations() ) {
decorator.addAnnotation( typeAnnotation );
}
decorator.removeConstructor();
List<Annotation> annotations = getDelegatorReferenceAnnotations( mapper );
List<Field> replacement = new ArrayList<Field>();
if ( !decorator.getMethods().isEmpty() ) {
for ( Field field : decorator.getFields() ) {
replacement.add( replacementMapperReference( field, annotations ) );
}
}
decorator.setFields( replacement );
}
protected List<Annotation> getDelegatorReferenceAnnotations(Mapper mapper) {
return Collections.emptyList();
}
/** /**
* @param originalReference the reference to be replaced * @param originalReference the reference to be replaced
* *
* @return the mapper reference replacing the original one * @return the mapper reference replacing the original one
*/ */
protected MapperReference replacementMapperReference(MapperReference originalReference) { protected MapperReference replacementMapperReference(Field originalReference, List<Annotation> annotations) {
return new AnnotationMapperReference( return new AnnotationMapperReference(
originalReference.getType(), originalReference.getType(),
originalReference.getVariableName(), originalReference.getVariableName(),
getMapperReferenceAnnotation(), annotations,
originalReference.isUsed() originalReference.isUsed()
); );
} }
@ -99,10 +131,17 @@ public abstract class AnnotationBasedComponentModelProcessor implements ModelEle
*/ */
protected abstract List<Annotation> getTypeAnnotations(Mapper mapper); protected abstract List<Annotation> getTypeAnnotations(Mapper mapper);
/**
* @return the annotation(s) to be added at the decorator of the mapper
*/
protected List<Annotation> getDecoratorAnnotations() {
return Collections.emptyList();
}
/** /**
* @return the annotation of the field for the mapper reference * @return the annotation of the field for the mapper reference
*/ */
protected abstract Annotation getMapperReferenceAnnotation(); protected abstract List<Annotation> getMapperReferenceAnnotations();
/** /**
* @return if a decorator (sub-)class needs to be generated or not * @return if a decorator (sub-)class needs to be generated or not

View File

@ -18,6 +18,7 @@
*/ */
package org.mapstruct.ap.internal.processor; package org.mapstruct.ap.internal.processor;
import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
@ -46,8 +47,8 @@ public class CdiComponentProcessor extends AnnotationBasedComponentModelProcesso
} }
@Override @Override
protected Annotation getMapperReferenceAnnotation() { protected List<Annotation> getMapperReferenceAnnotations() {
return new Annotation( getTypeFactory().getType( "javax.inject.Inject" ) ); return Arrays.asList( new Annotation( getTypeFactory().getType( "javax.inject.Inject" ) ) );
} }
@Override @Override

View File

@ -18,11 +18,13 @@
*/ */
package org.mapstruct.ap.internal.processor; package org.mapstruct.ap.internal.processor;
import org.mapstruct.ap.internal.model.Annotation; import java.util.Arrays;
import org.mapstruct.ap.internal.model.Mapper;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import org.mapstruct.ap.internal.model.Annotation;
import org.mapstruct.ap.internal.model.Mapper;
/** /**
* A {@link ModelElementProcessor} which converts the given {@link Mapper} * A {@link ModelElementProcessor} which converts the given {@link Mapper}
* object into a JSR 330 style bean in case "jsr330" is configured as the * object into a JSR 330 style bean in case "jsr330" is configured as the
@ -39,16 +41,46 @@ public class Jsr330ComponentProcessor extends AnnotationBasedComponentModelProce
@Override @Override
protected List<Annotation> getTypeAnnotations(Mapper mapper) { protected List<Annotation> getTypeAnnotations(Mapper mapper) {
return Collections.singletonList( new Annotation( getTypeFactory().getType( "javax.inject.Named" ) ) ); if ( mapper.getDecorator() == null ) {
return Collections.singletonList( named() );
}
else {
return Collections.singletonList( namedDelegate( mapper ) );
}
} }
@Override @Override
protected Annotation getMapperReferenceAnnotation() { protected List<Annotation> getDecoratorAnnotations() {
return new Annotation( getTypeFactory().getType( "javax.inject.Inject" ) ); return Collections.singletonList( named() );
}
@Override
protected List<Annotation> getDelegatorReferenceAnnotations(Mapper mapper) {
return Arrays.asList( inject(), namedDelegate( mapper ) );
}
@Override
protected List<Annotation> getMapperReferenceAnnotations() {
return Collections.singletonList( inject() );
} }
@Override @Override
protected boolean requiresGenerationOfDecoratorClass() { protected boolean requiresGenerationOfDecoratorClass() {
return true; return true;
} }
private Annotation named() {
return new Annotation( getTypeFactory().getType( "javax.inject.Named" ) );
}
private Annotation namedDelegate(Mapper mapper) {
return new Annotation(
getTypeFactory().getType( "javax.inject.Named" ),
Collections.singletonList( '"' + mapper.getPackageName() + "." + mapper.getName() + '"' )
);
}
private Annotation inject() {
return new Annotation( getTypeFactory().getType( "javax.inject.Inject" ) );
}
} }

View File

@ -20,6 +20,7 @@ package org.mapstruct.ap.internal.processor;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.List; import java.util.List;
import org.mapstruct.ap.internal.model.Annotation; import org.mapstruct.ap.internal.model.Annotation;
@ -42,26 +43,58 @@ public class SpringComponentProcessor extends AnnotationBasedComponentModelProce
@Override @Override
protected List<Annotation> getTypeAnnotations(Mapper mapper) { protected List<Annotation> getTypeAnnotations(Mapper mapper) {
List<Annotation> typeAnnotations = new ArrayList<Annotation>(); List<Annotation> typeAnnotations = new ArrayList<Annotation>();
typeAnnotations.add( new Annotation( getTypeFactory().getType( "org.springframework.stereotype.Component" ) ) ); typeAnnotations.add( component() );
if ( mapper.getDecorator() != null ) { if ( mapper.getDecorator() != null ) {
Annotation qualifier = new Annotation( typeAnnotations.add( qualifierDelegate() );
getTypeFactory().getType( "org.springframework.beans.factory.annotation.Qualifier" ),
Arrays.asList( "\"delegate\"" )
);
typeAnnotations.add( qualifier );
} }
return typeAnnotations; return typeAnnotations;
} }
@Override @Override
protected Annotation getMapperReferenceAnnotation() { protected List<Annotation> getDecoratorAnnotations() {
return new Annotation( getTypeFactory().getType( "org.springframework.beans.factory.annotation.Autowired" ) ); return Arrays.asList(
component(),
primary()
);
}
@Override
protected List<Annotation> getMapperReferenceAnnotations() {
return Collections.singletonList(
autowired()
);
}
@Override
protected List<Annotation> getDelegatorReferenceAnnotations(Mapper mapper) {
return Arrays.asList(
autowired(),
qualifierDelegate()
);
} }
@Override @Override
protected boolean requiresGenerationOfDecoratorClass() { protected boolean requiresGenerationOfDecoratorClass() {
return false; return true;
}
private Annotation autowired() {
return new Annotation( getTypeFactory().getType( "org.springframework.beans.factory.annotation.Autowired" ) );
}
private Annotation qualifierDelegate() {
return new Annotation(
getTypeFactory().getType( "org.springframework.beans.factory.annotation.Qualifier" ),
Collections.singletonList( "\"delegate\"" ) );
}
private Annotation primary() {
return new Annotation( getTypeFactory().getType( "org.springframework.context.annotation.Primary" ) );
}
private Annotation component() {
return new Annotation( getTypeFactory().getType( "org.springframework.stereotype.Component" ) );
} }
} }

View File

@ -18,5 +18,7 @@
limitations under the License. limitations under the License.
--> -->
<#list annotations as annotation>
<#nt><@includeModel object=annotation/> <#nt><@includeModel object=annotation/>
</#list>
private <@includeModel object=type/> ${variableName}; private <@includeModel object=type/> ${variableName};

View File

@ -0,0 +1,110 @@
/**
* Copyright 2012-2015 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.jsr330;
import java.util.Calendar;
import javax.inject.Inject;
import javax.inject.Named;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.test.decorator.Address;
import org.mapstruct.ap.test.decorator.AddressDto;
import org.mapstruct.ap.test.decorator.Person;
import org.mapstruct.ap.test.decorator.PersonDto;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import static org.fest.assertions.Assertions.assertThat;
/**
* Test for the application of decorators using component model jsr330.
*
* @author Andreas Gudian
*/
@WithClasses({
Person.class,
PersonDto.class,
Address.class,
AddressDto.class,
PersonMapper.class,
PersonMapperDecorator.class
})
@IssueKey("592")
@RunWith(AnnotationProcessorTestRunner.class)
@ComponentScan(basePackageClasses = Jsr330DecoratorTest.class)
@Configuration
public class Jsr330DecoratorTest {
@Inject
@Named
private PersonMapper personMapper;
private ConfigurableApplicationContext context;
@Before
public void springUp() {
context = new AnnotationConfigApplicationContext( getClass() );
context.getAutowireCapableBeanFactory().autowireBean( this );
}
@After
public void springDown() {
if ( context != null ) {
context.close();
}
}
@Test
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.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
public void shouldDelegateNonDecoratedMethodsToDefaultImplementation() {
//given
Address address = new Address( "42 Ocean View Drive" );
//when
AddressDto addressDto = personMapper.addressToAddressDto( address );
//then
assertThat( addressDto ).isNotNull();
assertThat( addressDto.getAddressLine() ).isEqualTo( "42 Ocean View Drive" );
}
}

View File

@ -0,0 +1,37 @@
/**
* Copyright 2012-2015 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.jsr330;
import org.mapstruct.DecoratedWith;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ap.test.decorator.Address;
import org.mapstruct.ap.test.decorator.AddressDto;
import org.mapstruct.ap.test.decorator.Person;
import org.mapstruct.ap.test.decorator.PersonDto;
@Mapper(componentModel = "jsr330")
@DecoratedWith(PersonMapperDecorator.class)
public interface PersonMapper {
@Mapping( target = "name", ignore = true )
PersonDto personToPersonDto(Person person);
AddressDto addressToAddressDto(Address address);
}

View File

@ -0,0 +1,40 @@
/**
* Copyright 2012-2015 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.jsr330;
import javax.inject.Inject;
import javax.inject.Named;
import org.mapstruct.ap.test.decorator.Person;
import org.mapstruct.ap.test.decorator.PersonDto;
public abstract class PersonMapperDecorator implements PersonMapper {
@Inject
@Named("org.mapstruct.ap.test.decorator.jsr330.PersonMapperImpl_")
private PersonMapper 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,37 @@
/**
* Copyright 2012-2015 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.spring;
import org.mapstruct.DecoratedWith;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.ap.test.decorator.Address;
import org.mapstruct.ap.test.decorator.AddressDto;
import org.mapstruct.ap.test.decorator.Person;
import org.mapstruct.ap.test.decorator.PersonDto;
@Mapper(componentModel = "spring")
@DecoratedWith(PersonMapperDecorator.class)
public interface PersonMapper {
@Mapping( target = "name", ignore = true )
PersonDto personToPersonDto(Person person);
AddressDto addressToAddressDto(Address address);
}

View File

@ -0,0 +1,39 @@
/**
* Copyright 2012-2015 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.spring;
import org.mapstruct.ap.test.decorator.Person;
import org.mapstruct.ap.test.decorator.PersonDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
public abstract class PersonMapperDecorator implements PersonMapper {
@Autowired
@Qualifier("delegate")
private PersonMapper 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,107 @@
/**
* Copyright 2012-2015 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.spring;
import java.util.Calendar;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.test.decorator.Address;
import org.mapstruct.ap.test.decorator.AddressDto;
import org.mapstruct.ap.test.decorator.Person;
import org.mapstruct.ap.test.decorator.PersonDto;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import static org.fest.assertions.Assertions.assertThat;
/**
* Test for the application of decorators using component model spring.
*
* @author Andreas Gudian
*/
@WithClasses({
Person.class,
PersonDto.class,
Address.class,
AddressDto.class,
PersonMapper.class,
PersonMapperDecorator.class
})
@IssueKey("592")
@RunWith(AnnotationProcessorTestRunner.class)
@ComponentScan(basePackageClasses = SpringDecoratorTest.class)
@Configuration
public class SpringDecoratorTest {
@Autowired
private PersonMapper personMapper;
private ConfigurableApplicationContext context;
@Before
public void springUp() {
context = new AnnotationConfigApplicationContext( getClass() );
context.getAutowireCapableBeanFactory().autowireBean( this );
}
@After
public void springDown() {
if ( context != null ) {
context.close();
}
}
@Test
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.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
public void shouldDelegateNonDecoratedMethodsToDefaultImplementation() {
//given
Address address = new Address( "42 Ocean View Drive" );
//when
AddressDto addressDto = personMapper.addressToAddressDto( address );
//then
assertThat( addressDto ).isNotNull();
assertThat( addressDto.getAddressLine() ).isEqualTo( "42 Ocean View Drive" );
}
}