#383 introduction of @InheritConfiguration to align update with create methods (single source)

This commit is contained in:
sjaakd 2014-12-20 14:59:22 +01:00 committed by Andreas Gudian
parent 3e81651455
commit b2ef82ba5c
19 changed files with 1185 additions and 18 deletions

View File

@ -0,0 +1,49 @@
/**
* 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;
/**
* Advises the code generator to apply all the {@link Mapping}s from a mapping method to the annotated method
* as well. The method should have the same source type and target type.
* <p>
* A typical use case is annotating an update method (a method with the {@link MappingTarget} in which an existing
* target is updated) in order to copy all defined mappings.
* <p>
* If more than one matching method exists, the name of the method to inherit the configuration from must be
* specified via {@link #name()}
*
* @author Sjaak Derksen
*/
@Target( ElementType.METHOD )
@Retention( RetentionPolicy.SOURCE )
public @interface InheritConfiguration {
/**
* The name of the mapping method to inherit the mappings from. Needs only to be specified in case more than
* one method with matching source and target type exists.
*
* @return The name of the mapping method to inherit the mappings from.
*/
String name() default "";
}

View File

@ -279,6 +279,14 @@ public class SourceMethod implements Method {
&& equals( getResultType(), method.getSourceParameters().iterator().next().getType() );
}
public boolean isSame(SourceMethod method) {
return getSourceParameters().size() == 1 && method.getSourceParameters().size() == 1
&& equals( getSourceParameters().iterator().next().getType(),
method.getSourceParameters().iterator().next().getType())
&& equals( getResultType(), method.getResultType() );
}
/**
* {@inheritDoc} {@link Method}
*/
@ -405,13 +413,15 @@ public class SourceMethod implements Method {
/**
* Merges in all the mappings configured via the given inverse mapping method, giving the locally defined mappings
* precedence.
* @param inverseMethod
* @param templateMethod
*/
public void mergeWithInverseMappings(SourceMethod inverseMethod) {
public void mergeWithInverseMappings(SourceMethod inverseMethod, SourceMethod templateMethod) {
Map<String, List<Mapping>> newMappings = new HashMap<String, List<Mapping>>();
if ( inverseMethod != null && !inverseMethod.getMappings().isEmpty() ) {
for ( List<Mapping> mappings : inverseMethod.getMappings().values() ) {
for ( Mapping inverseMapping : mappings ) {
for ( List<Mapping> lmappings : inverseMethod.getMappings().values() ) {
for ( Mapping inverseMapping : lmappings ) {
Mapping reversed = inverseMapping.reverse( this, messager, typeFactory );
if ( reversed != null ) {
List<Mapping> mappingsOfProperty = newMappings.get( reversed.getTargetName() );
@ -425,6 +435,21 @@ public class SourceMethod implements Method {
}
}
if ( templateMethod != null && !templateMethod.getMappings().isEmpty() ) {
for ( List<Mapping> lmappings : templateMethod.getMappings().values() ) {
for ( Mapping templateMapping : lmappings ) {
if ( templateMapping != null ) {
List<Mapping> mappingsOfProperty = newMappings.get( templateMapping.getTargetName() );
if ( mappingsOfProperty == null ) {
mappingsOfProperty = new ArrayList<Mapping>();
newMappings.put( templateMapping.getTargetName(), mappingsOfProperty );
}
mappingsOfProperty.add( templateMapping );
}
}
}
}
if ( getMappings().isEmpty() ) {
// the mapping method is configuredByReverseMappingMethod, see SourceMethod#setMappings()
setMappings( newMappings );

View File

@ -23,6 +23,7 @@ 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.InheritConfiguration;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.IterableMapping;
import org.mapstruct.MapMapping;
@ -50,6 +51,7 @@ import org.mapstruct.TargetType;
@GeneratePrism(value = MappingTarget.class, publicAccess = true),
@GeneratePrism(value = DecoratedWith.class, publicAccess = true),
@GeneratePrism(value = MapperConfig.class, publicAccess = true),
@GeneratePrism(value = InheritConfiguration.class, publicAccess = true),
@GeneratePrism(value = InheritInverseConfiguration.class, publicAccess = true),
@GeneratePrism(value = Qualifier.class, publicAccess = true),
@GeneratePrism(value = NullValueMapping.class, publicAccess = true),

View File

@ -50,6 +50,7 @@ import org.mapstruct.ap.model.common.TypeFactory;
import org.mapstruct.ap.model.source.SourceMethod;
import org.mapstruct.ap.option.Options;
import org.mapstruct.ap.prism.DecoratedWithPrism;
import org.mapstruct.ap.prism.InheritConfigurationPrism;
import org.mapstruct.ap.prism.InheritInverseConfigurationPrism;
import org.mapstruct.ap.prism.MapperPrism;
import org.mapstruct.ap.processor.creation.MappingResolverImpl;
@ -247,15 +248,20 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
}
SourceMethod inverseMappingMethod = getInverseMappingMethod( methods, method );
SourceMethod templateMappingMethod = getTemplateMappingMethod( methods, method );
boolean hasFactoryMethod = false;
if ( method.isIterableMapping() ) {
IterableMappingMethod.Builder builder = new IterableMappingMethod.Builder();
if ( method.getIterableMapping() == null && inverseMappingMethod != null &&
inverseMappingMethod.getIterableMapping() != null ) {
if ( method.getIterableMapping() == null ) {
if ( inverseMappingMethod != null && inverseMappingMethod.getIterableMapping() != null ) {
method.setIterableMapping( inverseMappingMethod.getIterableMapping() );
}
else if ( templateMappingMethod != null && templateMappingMethod.getIterableMapping() != null ) {
method.setIterableMapping( templateMappingMethod.getIterableMapping() );
}
}
String dateFormat = null;
List<TypeMirror> qualifiers = null;
@ -278,10 +284,14 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
MapMappingMethod.Builder builder = new MapMappingMethod.Builder();
if ( method.getMapMapping() == null && inverseMappingMethod != null &&
inverseMappingMethod.getMapMapping() != null ) {
if ( method.getMapMapping() == null ) {
if ( inverseMappingMethod != null && inverseMappingMethod.getMapMapping() != null ) {
method.setMapMapping( inverseMappingMethod.getMapMapping() );
}
else if ( templateMappingMethod != null && templateMappingMethod.getMapMapping() != null ) {
method.setMapMapping( templateMappingMethod.getMapMapping() );
}
}
String keyDateFormat = null;
String valueDateFormat = null;
List<TypeMirror> keyQualifiers = null;
@ -308,7 +318,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
else if ( method.isEnumMapping() ) {
EnumMappingMethod.Builder builder = new EnumMappingMethod.Builder();
method.mergeWithInverseMappings( inverseMappingMethod );
method.mergeWithInverseMappings( inverseMappingMethod, templateMappingMethod );
MappingMethod enumMappingMethod = builder
.mappingContext( mappingContext )
.souceMethod( method )
@ -321,7 +331,7 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
else {
BeanMappingMethod.Builder builder = new BeanMappingMethod.Builder();
method.mergeWithInverseMappings( inverseMappingMethod );
method.mergeWithInverseMappings( inverseMappingMethod, templateMappingMethod );
BeanMappingMethod beanMappingMethod = builder
.mappingContext( mappingContext )
.souceMethod( method )
@ -422,6 +432,90 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
return result;
}
/**
* Returns the configuring forward method in case the given method is annotated with
* {@code @InheritConfiguration} and exactly one such configuring method can unambiguously be selected (as
* per the source/target type and optionally the name given via {@code @InheritConfiguration}).
*
* The method cannot be marked forward mapping itself (hence 'ohter'). And neither can it contain an
* {@code @InheritReverseConfiguration}
*/
private SourceMethod getTemplateMappingMethod(List<SourceMethod> rawMethods, SourceMethod method) {
SourceMethod result = null;
InheritConfigurationPrism forwardPrism = InheritConfigurationPrism.getInstanceOn(
method.getExecutable()
);
if (forwardPrism != null) {
reportErrorWhenInheritForwardAlsoHasInheritReverseMapping( method );
// method is configured as being reverse method, collect candidates
List<SourceMethod> candidates = new ArrayList<SourceMethod>();
for ( SourceMethod oneMethod : rawMethods ) {
// method must be similar but not equal
if ( oneMethod.isSame( method ) && !( oneMethod.equals( method ) ) ) {
candidates.add( oneMethod );
}
}
String name = forwardPrism.name();
if ( candidates.size() == 1 ) {
// no ambiguity: if no configuredBy is specified, or configuredBy specified and match
if ( name.isEmpty() ) {
result = candidates.get( 0 );
}
else if ( candidates.get( 0 ).getName().equals( name ) ) {
result = candidates.get( 0 );
}
else {
reportErrorWhenNonMatchingName( candidates.get( 0 ), method, forwardPrism );
}
}
else if ( candidates.size() > 1 ) {
// ambiguity: find a matching method that matches configuredBy
List<SourceMethod> nameFilteredcandidates = new ArrayList<SourceMethod>();
for ( SourceMethod candidate : candidates ) {
if ( candidate.getName().equals( name ) ) {
nameFilteredcandidates.add( candidate );
}
}
if ( nameFilteredcandidates.size() == 1 ) {
result = nameFilteredcandidates.get( 0 );
}
else if ( nameFilteredcandidates.size() > 1 ) {
reportErrorWhenSeveralNamesMatch( nameFilteredcandidates, method, forwardPrism );
}
if ( result == null ) {
reportErrorWhenAmbigousMapping( candidates, method, forwardPrism );
}
}
if ( result != null ) {
reportErrorIfMethodHasAnnotation( result, method, forwardPrism );
}
}
return result;
}
private void reportErrorWhenInheritForwardAlsoHasInheritReverseMapping(SourceMethod method) {
InheritInverseConfigurationPrism reversePrism = InheritInverseConfigurationPrism.getInstanceOn(
method.getExecutable()
);
if ( reversePrism != null ) {
messager.printMessage(
Diagnostic.Kind.ERROR,
"Method cannot be annotated with both a @InheritConfiguration and @InheritInverseConfiguration",
method.getExecutable(),
reversePrism.mirror
);
}
}
private void reportErrorIfInverseMethodHasInheritAnnotation(SourceMethod candidate,
SourceMethod method,
InheritInverseConfigurationPrism reversePrism) {
@ -442,6 +536,21 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
reversePrism.mirror
);
}
InheritConfigurationPrism candidateTemplatePrism = InheritConfigurationPrism.getInstanceOn(
candidate.getExecutable()
);
if ( candidateTemplatePrism != null ) {
messager.printMessage(
Diagnostic.Kind.ERROR,
String.format(
"Resolved inverse mapping method %s() should not carry the @InheritConfiguration annotation.",
candidate.getName()
),
method.getExecutable(),
reversePrism.mirror
);
}
}
private void reportErrorWhenAmbigousReverseMapping(List<SourceMethod> candidates, SourceMethod method,
@ -504,4 +613,104 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
reversePrism.mirror
);
}
private void reportErrorIfMethodHasAnnotation(SourceMethod candidate,
SourceMethod method,
InheritConfigurationPrism prism) {
InheritConfigurationPrism candidatePrism = InheritConfigurationPrism.getInstanceOn(
candidate.getExecutable()
);
if ( candidatePrism != null ) {
messager.printMessage(
Diagnostic.Kind.ERROR,
String.format(
"Resolved mapping method %s() should not carry the @InheritConfiguration annotation itself.",
candidate.getName()
),
method.getExecutable(),
prism.mirror
);
}
InheritInverseConfigurationPrism candidateInversePrism = InheritInverseConfigurationPrism.getInstanceOn(
candidate.getExecutable()
);
if ( candidateInversePrism != null ) {
messager.printMessage(
Diagnostic.Kind.ERROR,
String.format(
"Resolved mapping method %s() should not carry the @InheritInverseConfiguration annotation.",
candidate.getName()
),
method.getExecutable(),
prism.mirror
);
}
}
private void reportErrorWhenAmbigousMapping(List<SourceMethod> candidates, SourceMethod method,
InheritConfigurationPrism prism) {
List<String> candidateNames = new ArrayList<String>();
for ( SourceMethod candidate : candidates ) {
candidateNames.add( candidate.getName() );
}
String name = prism.name();
if ( name.isEmpty() ) {
messager.printMessage(
Diagnostic.Kind.ERROR,
String.format(
"Several matching methods exist: %s(). Specify a name explicitly.",
Strings.join( candidateNames, "(), " )
),
method.getExecutable(),
prism.mirror
);
}
else {
messager.printMessage(
Diagnostic.Kind.ERROR,
String.format(
"None of the candidates %s() matches given name: \"%s\".",
Strings.join( candidateNames, "(), " ), name
),
method.getExecutable(),
prism.mirror
);
}
}
private void reportErrorWhenSeveralNamesMatch(List<SourceMethod> candidates, SourceMethod method,
InheritConfigurationPrism prism) {
messager.printMessage(
Diagnostic.Kind.ERROR,
String.format(
"Given name \"%s\" matches several candidate methods: %s().",
prism.name(), Strings.join( candidates, "(), " )
),
method.getExecutable(),
prism.mirror
);
}
private void reportErrorWhenNonMatchingName(SourceMethod onlyCandidate, SourceMethod method,
InheritConfigurationPrism prims) {
messager.printMessage(
Diagnostic.Kind.ERROR,
String.format(
"Given name \"%s\" does not match the only candidate. Did you mean: \"%s\".",
prims.name(), onlyCandidate.getName()
),
method.getExecutable(),
prims.mirror
);
}
}

View File

@ -120,18 +120,43 @@ public class InheritInverseConfigurationTest {
}
@Test
@WithClasses({ SourceTargetMapperErroneouslyAnnotated.class })
@WithClasses({ SourceTargetMapperErroneouslyAnnotated1.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = SourceTargetMapperErroneouslyAnnotated.class,
@Diagnostic(type = SourceTargetMapperErroneouslyAnnotated1.class,
kind = Kind.ERROR,
line = 49,
line = 50,
messageRegExp = "Resolved inverse mapping method reverse\\(\\) should not carry the "
+ "@InheritInverseConfiguration annotation itself.")
}
)
public void shouldUseWronglyAnnotatedError() {
public void shouldUseWronglyAnnotatedError1() {
}
@Test
@WithClasses({ SourceTargetMapperErroneouslyAnnotated2.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = SourceTargetMapperErroneouslyAnnotated2.class,
kind = Kind.WARNING,
line = 45,
messageRegExp = "Unmapped target properties: \"stringPropX, integerPropX, "
+ "someConstantDownstream, propertyToIgnoreDownstream\""),
@Diagnostic(type = SourceTargetMapperErroneouslyAnnotated2.class,
kind = Kind.ERROR,
line = 47,
messageRegExp = "Resolved inverse mapping method forward2\\(\\) should not carry "
+ "the @InheritConfiguration annotation"),
@Diagnostic(type = SourceTargetMapperErroneouslyAnnotated2.class,
kind = Kind.WARNING,
line = 48,
messageRegExp = "Unmapped target properties: \"stringPropY, integerPropY, "
+ "propertyNotToIgnoreUpstream\"")
}
)
public void shouldUseWronglyAnnotatedError2() {
}
@Test
@ -152,4 +177,5 @@ public class InheritInverseConfigurationTest {
)
public void shouldAdviseOnSpecifyingCorrectName() {
}
}

View File

@ -28,9 +28,10 @@ import org.mapstruct.factory.Mappers;
* @author Sjaak Derksen
*/
@Mapper
public interface SourceTargetMapperErroneouslyAnnotated {
public interface SourceTargetMapperErroneouslyAnnotated1 {
SourceTargetMapperErroneouslyAnnotated INSTANCE = Mappers.getMapper( SourceTargetMapperErroneouslyAnnotated.class );
SourceTargetMapperErroneouslyAnnotated1 INSTANCE =
Mappers.getMapper( SourceTargetMapperErroneouslyAnnotated1.class );
@Mappings({
@Mapping(source = "stringPropX", target = "stringPropY"),

View File

@ -0,0 +1,49 @@
/**
* 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.reverse;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
*/
@Mapper
public interface SourceTargetMapperErroneouslyAnnotated2 {
SourceTargetMapperErroneouslyAnnotated2 INSTANCE
= Mappers.getMapper( SourceTargetMapperErroneouslyAnnotated2.class );
@Mappings({
@Mapping(source = "stringPropX", target = "stringPropY"),
@Mapping(source = "integerPropX", target = "integerPropY"),
@Mapping(source = "propertyToIgnoreDownstream", target = "propertyNotToIgnoreUpstream")
})
Target forward(Source source);
@InheritConfiguration(name = "forward2")
Source forward2(Target target);
@InheritInverseConfiguration(name = "forward2")
Target reverse(Source source);
}

View File

@ -0,0 +1,205 @@
/**
* 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.template;
import javax.tools.Diagnostic.Kind;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
import static org.fest.assertions.Assertions.assertThat;
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
/**
* @author Sjaak Derksen
*/
@IssueKey("383")
@WithClasses({ Source.class, NestedSource.class, Target.class })
@RunWith(AnnotationProcessorTestRunner.class)
public class InheritConfigurationTest {
@Test
@WithClasses({ SourceTargetMapperSingle.class })
public void shouldInheritConfigurationSingleCandidates() {
Source source = new Source();
source.setStringPropX( "1" );
source.setIntegerPropX( 2 );
source.setNestedSourceProp( new NestedSource("nested") );
Target createdTarget = SourceTargetMapperSingle.INSTANCE.forwardCreate( source );
assertThat( createdTarget ).isNotNull();
assertThat( createdTarget.getStringPropY() ).isEqualTo( "1" );
assertThat( createdTarget.getIntegerPropY() ).isEqualTo( 2 );
assertThat( createdTarget.getNestedResultProp() ).isEqualTo( "nested");
assertThat( createdTarget.getExpressionProp() ).isEqualTo( "expression" );
assertThat( createdTarget.getConstantProp() ).isEqualTo( "constant" );
Target updatedTarget = new Target();
SourceTargetMapperSingle.INSTANCE.forwardUpdate( source, updatedTarget );
assertThat( updatedTarget ).isNotNull();
assertThat( updatedTarget.getStringPropY() ).isEqualTo( "1" );
assertThat( updatedTarget.getIntegerPropY() ).isEqualTo( 2 );
assertThat( updatedTarget.getNestedResultProp() ).isEqualTo( "nested" );
assertThat( updatedTarget.getExpressionProp() ).isEqualTo( "expression" );
assertThat( updatedTarget.getConstantProp() ).isEqualTo( "constant" );
}
@Test
@WithClasses({ SourceTargetMapperMultiple.class })
public void shouldInheritConfigurationMultipleCandidates() {
Source source = new Source();
source.setStringPropX( "1" );
source.setIntegerPropX( 2 );
source.setNestedSourceProp( new NestedSource("nested") );
Target createdTarget = SourceTargetMapperSingle.INSTANCE.forwardCreate( source );
assertThat( createdTarget ).isNotNull();
assertThat( createdTarget.getStringPropY() ).isEqualTo( "1" );
assertThat( createdTarget.getIntegerPropY() ).isEqualTo( 2 );
assertThat( createdTarget.getNestedResultProp() ).isEqualTo( "nested");
assertThat( createdTarget.getExpressionProp() ).isEqualTo( "expression" );
assertThat( createdTarget.getConstantProp() ).isEqualTo( "constant" );
Target updatedTarget = new Target();
SourceTargetMapperSingle.INSTANCE.forwardUpdate( source, updatedTarget );
assertThat( updatedTarget ).isNotNull();
assertThat( updatedTarget.getStringPropY() ).isEqualTo( "1" );
assertThat( updatedTarget.getIntegerPropY() ).isEqualTo( 2 );
assertThat( updatedTarget.getNestedResultProp() ).isEqualTo( "nested" );
assertThat( updatedTarget.getExpressionProp() ).isEqualTo( "expression" );
assertThat( updatedTarget.getConstantProp() ).isEqualTo( "constant" );
}
@Test
@WithClasses({ SourceTargetMapperAmbiguous1.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = SourceTargetMapperAmbiguous1.class,
kind = Kind.ERROR,
line = 54,
messageRegExp = "Several matching methods exist: forwardCreate\\(\\), "
+ "forwardCreate1\\(\\). Specify a name explicitly."),
@Diagnostic(type = SourceTargetMapperAmbiguous1.class,
kind = Kind.WARNING,
line = 55,
messageRegExp = "Unmapped target properties: \"stringPropY, nestedResultProp, integerPropY, "
+ "constantProp, expressionProp\"")
}
)
public void shouldRaiseAmbiguousReverseMethodError() {
}
@Test
@WithClasses({ SourceTargetMapperAmbiguous2.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = SourceTargetMapperAmbiguous2.class,
kind = Kind.ERROR,
line = 54,
messageRegExp = "None of the candidates forwardCreate\\(\\), forwardCreate1\\(\\) matches given "
+ "name: \"blah\"."),
@Diagnostic(type = SourceTargetMapperAmbiguous2.class,
kind = Kind.WARNING,
line = 55,
messageRegExp = "Unmapped target properties: \"stringPropY, nestedResultProp, integerPropY, "
+ "constantProp, expressionProp\"")
}
)
public void shouldRaiseAmbiguousReverseMethodErrorWrongName() {
}
@Test
@WithClasses({ SourceTargetMapperAmbiguous3.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = SourceTargetMapperAmbiguous3.class,
kind = Kind.ERROR,
line = 54,
messageRegExp = "Given name \"forwardCreate\" matches several candidate methods: "
+ ".*forwardCreate.*\\(\\), .*forwardCreate.*\\(\\)"),
@Diagnostic(type = SourceTargetMapperAmbiguous3.class,
kind = Kind.WARNING,
line = 55,
messageRegExp = "Unmapped target properties: \"stringPropY, nestedResultProp, integerPropY, "
+ "constantProp, expressionProp\"") }
)
public void shouldRaiseAmbiguousReverseMethodErrorDuplicatedName() {
}
@Test
@WithClasses({ SourceTargetMapperErroneouslyAnnotated1.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = SourceTargetMapperErroneouslyAnnotated1.class,
kind = Kind.ERROR,
line = 49,
messageRegExp = "Resolved mapping method forwardCreate1\\(\\) should not carry the "
+ "@InheritConfiguration annotation itself.")
}
)
public void shouldUseWronglyAnnotatedError1() {
}
@Test
@WithClasses({ SourceTargetMapperErroneouslyAnnotated2.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = SourceTargetMapperErroneouslyAnnotated2.class,
kind = Kind.ERROR,
line = 51,
messageRegExp = "Resolved mapping method forwardCreate\\(\\) should not carry the "
+ "@InheritInverseConfiguration annotation.")
}
)
public void shouldUseWronglyAnnotatedError2() {
}
@Test
@WithClasses({ SourceTargetMapperNonMatchingName.class })
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = SourceTargetMapperNonMatchingName.class,
kind = Kind.ERROR,
line = 45,
messageRegExp = "Given name \"blah\" does not match the only candidate. Did you mean: "
+ "\"forwardCreate\"."),
@Diagnostic(type = SourceTargetMapperNonMatchingName.class,
kind = Kind.WARNING,
line = 46,
messageRegExp = "Unmapped target properties: \"stringPropY, nestedResultProp, integerPropY, "
+ "constantProp, expressionProp\"")
}
)
public void shouldAdviseOnSpecifyingCorrectName() {
}
}

View File

@ -0,0 +1,42 @@
/**
* 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.template;
/**
*
* @author Sjaak Derksen
*/
public class NestedSource {
private String nested;
public NestedSource(String nested) {
this.nested = nested;
}
public String getNested() {
return nested;
}
public void setNested(String nested) {
this.nested = nested;
}
}

View File

@ -0,0 +1,57 @@
/**
* 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.template;
/**
* @author Sjaak Derksen
*/
public class Source {
private String stringPropX;
private Integer integerPropX;
private NestedSource nestedSourceProp;
public String getStringPropX() {
return stringPropX;
}
public void setStringPropX(String stringPropX) {
this.stringPropX = stringPropX;
}
public Integer getIntegerPropX() {
return integerPropX;
}
public void setIntegerPropX(Integer integerPropX) {
this.integerPropX = integerPropX;
}
public NestedSource getNestedSourceProp() {
return nestedSourceProp;
}
public void setNestedSourceProp(NestedSource nestedSourceProp) {
this.nestedSourceProp = nestedSourceProp;
}
}

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.ap.test.template;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
*/
@Mapper
public interface SourceTargetMapperAmbiguous1 {
SourceTargetMapperAmbiguous1 INSTANCE = Mappers.getMapper( SourceTargetMapperAmbiguous1.class );
@Mappings({
@Mapping(target = "stringPropY", source = "stringPropX" ),
@Mapping(target = "integerPropY", source = "integerPropX" ),
@Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")")
})
Target forwardCreate(Source source);
@Mappings({
@Mapping(target = "stringPropY", source = "stringPropX" ),
@Mapping(target = "integerPropY", source = "integerPropX" ),
@Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")")
})
Target forwardCreate1(Source source);
@InheritConfiguration
void forwardUpdate(Source source, @MappingTarget Target target);
}

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.ap.test.template;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
*/
@Mapper
public interface SourceTargetMapperAmbiguous2 {
SourceTargetMapperAmbiguous2 INSTANCE = Mappers.getMapper( SourceTargetMapperAmbiguous2.class );
@Mappings({
@Mapping(target = "stringPropY", source = "stringPropX" ),
@Mapping(target = "integerPropY", source = "integerPropX" ),
@Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")")
})
Target forwardCreate(Source source);
@Mappings({
@Mapping(target = "stringPropY", source = "stringPropX" ),
@Mapping(target = "integerPropY", source = "integerPropX" ),
@Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")")
})
Target forwardCreate1(Source source);
@InheritConfiguration( name = "blah" )
void forwardUpdate(Source source, @MappingTarget Target target);
}

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.ap.test.template;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
*/
@Mapper
public interface SourceTargetMapperAmbiguous3 {
SourceTargetMapperAmbiguous3 INSTANCE = Mappers.getMapper( SourceTargetMapperAmbiguous3.class );
@Mappings({
@Mapping(target = "stringPropY", source = "stringPropX" ),
@Mapping(target = "integerPropY", source = "integerPropX" ),
@Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")"),
})
Target forwardCreate(Source source);
@Mappings({
@Mapping(target = "stringPropY", source = "stringPropX" ),
@Mapping(target = "integerPropY", source = "integerPropX" ),
@Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")"),
})
void forwardCreate(Source source, @MappingTarget Target target);
@InheritConfiguration(name = "forwardCreate")
void forwardUpdate(Source source, @MappingTarget Target target);
}

View File

@ -0,0 +1,51 @@
/**
* 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.template;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
*/
@Mapper
public interface SourceTargetMapperErroneouslyAnnotated1 {
SourceTargetMapperErroneouslyAnnotated1 INSTANCE =
Mappers.getMapper( SourceTargetMapperErroneouslyAnnotated1.class );
@Mappings({
@Mapping(target = "stringPropY", source = "stringPropX"),
@Mapping(target = "integerPropY", source = "integerPropX"),
@Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")")
})
Target forwardCreate(Source source);
@InheritConfiguration( name = "forwardCreate" )
Target forwardCreate1(Source source);
@InheritConfiguration( name = "forwardCreate1" )
void forwardUpdate(Source source, @MappingTarget Target target);
}

View File

@ -0,0 +1,54 @@
/**
* 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.template;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
*/
@Mapper
public interface SourceTargetMapperErroneouslyAnnotated2 {
SourceTargetMapperErroneouslyAnnotated2 INSTANCE =
Mappers.getMapper( SourceTargetMapperErroneouslyAnnotated2.class );
@Mappings({
@Mapping(target = "stringPropY", source = "stringPropX"),
@Mapping(target = "integerPropY", source = "integerPropX"),
@Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")")
})
Target forwardCreate(Source source);
@InheritInverseConfiguration(name = "forwardCreate")
@Mapping(target = "nestedSourceProp", ignore = true)
Source forwardCreate(Target source);
@InheritConfiguration( name = "forwardCreate" )
void forwardUpdate(Target source, @MappingTarget Source target);
}

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.ap.test.template;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
*/
@Mapper
public interface SourceTargetMapperMultiple {
SourceTargetMapperMultiple INSTANCE = Mappers.getMapper( SourceTargetMapperMultiple.class );
@Mappings({
@Mapping(target = "stringPropY", source = "stringPropX" ),
@Mapping(target = "integerPropY", source = "integerPropX" ),
@Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")"),
})
Target forwardCreate(Source source);
@Mappings({
@Mapping(target = "stringPropY", source = "stringPropX" ),
@Mapping(target = "integerPropY", source = "integerPropX" ),
@Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")"),
})
Target forwardCreate2(Source source);
@InheritConfiguration( name = "forwardCreate" )
void forwardUpdate(Source source, @MappingTarget Target target);
}

View File

@ -0,0 +1,47 @@
/**
* 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.template;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
*/
@Mapper
public interface SourceTargetMapperNonMatchingName {
SourceTargetMapperNonMatchingName INSTANCE = Mappers.getMapper( SourceTargetMapperNonMatchingName.class );
@Mappings({
@Mapping(target = "stringPropY", source = "stringPropX" ),
@Mapping(target = "integerPropY", source = "integerPropX" ),
@Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")")
})
Target forwardCreate(Source source);
@InheritConfiguration( name = "blah" )
void forwardUpdate(Source source, @MappingTarget Target target);
}

View File

@ -0,0 +1,49 @@
/**
* 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.template;
import org.mapstruct.InheritConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
/**
* @author Sjaak Derksen
*/
@Mapper
public interface SourceTargetMapperSingle {
SourceTargetMapperSingle INSTANCE = Mappers.getMapper( SourceTargetMapperSingle.class );
@Mappings({
@Mapping(target = "stringPropY", source = "stringPropX" ),
@Mapping(target = "integerPropY", source = "integerPropX" ),
@Mapping(target = "nestedResultProp", source = "nestedSourceProp.nested"),
@Mapping(target = "constantProp", constant = "constant"),
@Mapping(target = "expressionProp", expression = "java(\"expression\")"),
})
Target forwardCreate(Source source);
@InheritConfiguration
void forwardUpdate(Source source, @MappingTarget Target target);
}

View File

@ -0,0 +1,77 @@
/**
* 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.template;
/**
* @author Sjaak Derksen
*/
public class Target {
private String stringPropY;
private Integer integerPropY;
private String constantProp;
private String expressionProp;
private String nestedResultProp;
public String getStringPropY() {
return stringPropY;
}
public void setStringPropY(String stringPropY) {
this.stringPropY = stringPropY;
}
public Integer getIntegerPropY() {
return integerPropY;
}
public void setIntegerPropY(Integer integerPropY) {
this.integerPropY = integerPropY;
}
public String getConstantProp() {
return constantProp;
}
public void setConstantProp(String constantProp) {
this.constantProp = constantProp;
}
public String getExpressionProp() {
return expressionProp;
}
public void setExpressionProp(String expressionProp) {
this.expressionProp = expressionProp;
}
public String getNestedResultProp() {
return nestedResultProp;
}
public void setNestedResultProp(String nestedResultProp) {
this.nestedResultProp = nestedResultProp;
}
}