#1363 Add support for using default expression

This commit is contained in:
Jeff Smyth 2018-02-19 17:01:51 -05:00 committed by Filip Hrisafov
parent 8f88c6baa7
commit 48b9bd72be
16 changed files with 609 additions and 12 deletions

View File

@ -47,7 +47,8 @@ public @interface Mapper {
/**
* Additional types for which an import statement is to be added to the generated mapper implementation class.
* This allows to refer to those types from within mapping expressions given via {@link Mapping#expression()} using
* This allows to refer to those types from within mapping expressions given via {@link Mapping#expression()},
* {@link Mapping#defaultExpression()} or using
* their simple name rather than their fully-qualified name.
*
* @return classes to add in the imports of the generated implementation.

View File

@ -98,7 +98,8 @@ public @interface Mapping {
* property is not of type {@code String}, the value will be converted by applying a matching conversion method or
* built-in conversion.
* <p>
* This attribute can not be used together with {@link #source()}, {@link #defaultValue()} or {@link #expression()}.
* This attribute can not be used together with {@link #source()}, {@link #defaultValue()},
* {@link #defaultExpression()} or {@link #expression()}.
*
* @return A constant {@code String} constant specifying the value for the designated target property
*/
@ -123,12 +124,41 @@ public @interface Mapping {
* Any types referenced in expressions must be given via their fully-qualified name. Alternatively, types can be
* imported via {@link Mapper#imports()}.
* <p>
* This attribute can not be used together with {@link #source()}, {@link #defaultValue()} or {@link #constant()}.
* This attribute can not be used together with {@link #source()}, {@link #defaultValue()},
* {@link #defaultExpression()} or {@link #constant()}.
*
* @return An expression specifying the value for the designated target property
*/
String expression() default "";
/**
* A defaultExpression {@link String} based on which the specified target property is to be set
* if and only if the specified source property is null.
* <p>
* Currently, Java is the only supported "expression language" and expressions must be given in form of Java
* expressions using the following format: {@code java(<EXPRESSION>)}. For instance the mapping:
* <pre><code>
* &#64;Mapping(
* target = "someProp",
* defaultExpression = "java(new TimeAndFormat( s.getTime(), s.getFormat() ))"
* )
* </code></pre>
* <p>
* will cause the following target property assignment to be generated:
* <p>
* {@code targetBean.setSomeProp( new TimeAndFormat( s.getTime(), s.getFormat() ) )}.
* <p>
* Any types referenced in expressions must be given via their fully-qualified name. Alternatively, types can be
* imported via {@link Mapper#imports()}.
* <p>
* This attribute can not be used together with {@link #expression()}, {@link #defaultValue()}
* or {@link #constant()}.
*
* @return An expression specifying a defaultValue for the designated target property if the designated source
* property is null
*/
String defaultExpression() default "";
/**
* Whether the property specified via {@link #target()} should be ignored by the generated mapping method or not.
* This can be useful when certain attributes should not be propagated from source or target or when properties in
@ -186,7 +216,8 @@ public @interface Mapping {
* target property is not of type {@code String}, the value will be converted by applying a matching conversion
* method or built-in conversion.
* <p>
* This attribute can not be used together with {@link #constant()} or {@link #expression()}.
* This attribute can not be used together with {@link #constant()}, {@link #expression()}
* or {@link #defaultExpression()}.
*
* @return Default value to set in case the source property is {@code null}.
*/

View File

@ -96,7 +96,8 @@ public @interface Mapping {
* property is not of type {@code String}, the value will be converted by applying a matching conversion method or
* built-in conversion.
* <p>
* This attribute can not be used together with {@link #source()}, {@link #defaultValue()} or {@link #expression()}.
* This attribute can not be used together with {@link #source()}, {@link #defaultValue()},
* {@link #defaultExpression()} or {@link #expression()}.
*
* @return A constant {@code String} constant specifying the value for the designated target property
*/
@ -121,12 +122,41 @@ public @interface Mapping {
* Any types referenced in expressions must be given via their fully-qualified name. Alternatively, types can be
* imported via {@link Mapper#imports()}.
* <p>
* This attribute can not be used together with {@link #source()}, {@link #defaultValue()} or {@link #constant()}.
* This attribute can not be used together with {@link #source()}, {@link #defaultValue()},
* {@link #defaultExpression()} or {@link #constant()}.
*
* @return An expression specifying the value for the designated target property
*/
String expression() default "";
/**
* A defaultExpression {@link String} based on which the specified target property is to be set
* if and only if the specified source property is null.
* <p>
* Currently, Java is the only supported "expression language" and expressions must be given in form of Java
* expressions using the following format: {@code java(<EXPRESSION>)}. For instance the mapping:
* <pre><code>
* &#64;Mapping(
* target = "someProp",
* defaultExpression = "java(new TimeAndFormat( s.getTime(), s.getFormat() ))"
* )
* </code></pre>
* <p>
* will cause the following target property assignment to be generated:
* <p>
* {@code targetBean.setSomeProp( new TimeAndFormat( s.getTime(), s.getFormat() ) )}.
* <p>
* Any types referenced in expressions must be given via their fully-qualified name. Alternatively, types can be
* imported via {@link Mapper#imports()}.
* <p>
* This attribute can not be used together with {@link #expression()}, {@link #defaultValue()}
* or {@link #constant()}.
*
* @return An expression specifying a defaultValue for the designated target property if the designated source
* property is null
*/
String defaultExpression() default "";
/**
* Whether the property specified via {@link #target()} should be ignored by the generated mapping method or not.
* This can be useful when certain attributes should not be propagated from source or target or when properties in
@ -185,7 +215,8 @@ public @interface Mapping {
* target property is not of type {@code String}, the value will be converted by applying a matching conversion
* method or built-in conversion.
* <p>
* This attribute can not be used together with {@link #constant()} or {@link #expression()}.
* This attribute can not be used together with {@link #constant()}, {@link #expression()}
* or {@link #defaultExpression()}.
*
* @return Default value to set in case the source property is {@code null}.
*/

View File

@ -1824,7 +1824,7 @@ The String `"Constant Value"` is set as is to the target property `stringConstan
By means of Expressions it will be possible to include constructs from a number of languages.
Currently only Java is supported as language. This feature is e.g. useful to invoke constructors. The entire source object is available for usage in the expression. Care should be taken to insert only valid Java code: MapStruct will not validate the expression at generation-time, but errors will show up in the generated classes during compilation.
Currently only Java is supported as a language. This feature is e.g. useful to invoke constructors. The entire source object is available for usage in the expression. Care should be taken to insert only valid Java code: MapStruct will not validate the expression at generation-time, but errors will show up in the generated classes during compilation.
The example below demonstrates how two source properties can be mapped to one target:
@ -1866,6 +1866,35 @@ public interface SourceTargetMapper {
----
====
[[default-expressions]]
=== Default Expressions
Default expressions are a combination of default values and expressions. They will only be used when the source attribute is `null`.
The same warnings and restrictions apply to default expressions that apply to expressions. Only Java is supported, and MapStruct will not validate the expression at generation-time.
The example below demonstrates how two source properties can be mapped to one target:
.Mapping method using a default expression
===
[source, java, linenums]
[subs="verbatim,attributes"]
---
imports java.util.UUID;
@Mapper( imports = UUID.class )
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
@Mapping(target="id", source="sourceId", defaultExpression = "java( UUID.randomUUID().toString() )")
Target sourceToTarget(Source s);
}
---
===
The example demonstrates how to use defaultExpression to set an `ID` field if the source field is null, this could be used to take the existing `sourceId` from the source object if it is set, or create a new `Id` if it isn't. Please note that the fully qualified package name is specified because MapStruct does not take care of the import of the `UUID` class (unless its used otherwise explicitly in the `SourceTargetMapper`). This can be resolved by defining imports on the @Mapper annotation ((see <<expressions>>).
[[determining-result-type]]
=== Determining the result type

View File

@ -455,6 +455,7 @@ public class BeanMappingMethod extends NormalTypeMappingMethod {
.existingVariableNames( existingVariableNames )
.dependsOn( mapping.getDependsOn() )
.defaultValue( mapping.getDefaultValue() )
.defaultJavaExpression( mapping.getDefaultJavaExpression() )
.build();
handledTargets.add( propertyName );
unprocessedSourceParameters.remove( sourceRef.getParameter() );

View File

@ -188,6 +188,7 @@ public class PropertyMapping extends ModelElement {
// initial properties
private String defaultValue;
private String defaultJavaExpression;
private SourceReference sourceReference;
private SourceRHS rightHandSide;
private FormattingParameters formattingParameters;
@ -220,6 +221,11 @@ public class PropertyMapping extends ModelElement {
return this;
}
public PropertyMappingBuilder defaultJavaExpression(String defaultJavaExpression) {
this.defaultJavaExpression = defaultJavaExpression;
return this;
}
public PropertyMappingBuilder forgeMethodWithMappingOptions(MappingOptions mappingOptions) {
this.forgeMethodWithMappingOptions = mappingOptions;
return this;
@ -351,7 +357,7 @@ public class PropertyMapping extends ModelElement {
private Assignment getDefaultValueAssignment( Assignment rhs ) {
if ( defaultValue != null
&& ( !rhs.getSourceType().isPrimitive() || rhs.getSourcePresenceCheckerReference() != null) ) {
// cannot check on null source if source is primitive unless it has a presenche checker
// cannot check on null source if source is primitive unless it has a presence checker
PropertyMapping build = new ConstantMappingBuilder()
.constantExpression( '"' + defaultValue + '"' )
.formattingParameters( formattingParameters )
@ -366,6 +372,21 @@ public class PropertyMapping extends ModelElement {
.build();
return build.getAssignment();
}
if ( defaultJavaExpression != null
&& ( !rhs.getSourceType().isPrimitive() || rhs.getSourcePresenceCheckerReference() != null) ) {
// cannot check on null source if source is primitive unless it has a presence checker
PropertyMapping build = new JavaExpressionMappingBuilder()
.javaExpression( defaultJavaExpression )
.dependsOn( dependsOn )
.existingVariableNames( existingVariableNames )
.mappingContext( ctx )
.sourceMethod( method )
.targetPropertyName( targetPropertyName )
.targetReadAccessor( targetReadAccessor )
.targetWriteAccessor( targetWriteAccessor )
.build();
return build.getAssignment();
}
return null;
}

View File

@ -55,6 +55,7 @@ public class Mapping {
private final String sourceName;
private final String constant;
private final String javaExpression;
private final String defaultJavaExpression;
private final String targetName;
private final String defaultValue;
private final FormattingParameters formattingParameters;
@ -128,10 +129,23 @@ public class Mapping {
messager.printMessage( element, Message.PROPERTYMAPPING_CONSTANT_AND_DEFAULT_VALUE_BOTH_DEFINED );
return null;
}
else if ( mappingPrism.values.expression() != null && mappingPrism.values.defaultExpression() != null) {
messager.printMessage( element, Message.PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_EXPRESSION_BOTH_DEFINED );
return null;
}
else if ( mappingPrism.values.constant() != null && mappingPrism.values.defaultExpression() != null) {
messager.printMessage( element, Message.PROPERTYMAPPING_CONSTANT_AND_DEFAULT_EXPRESSION_BOTH_DEFINED );
return null;
}
else if ( mappingPrism.values.defaultValue() != null && mappingPrism.values.defaultExpression() != null) {
messager.printMessage( element, Message.PROPERTYMAPPING_DEFAULT_VALUE_AND_DEFAULT_EXPRESSION_BOTH_DEFINED );
return null;
}
String source = mappingPrism.source().isEmpty() ? null : mappingPrism.source();
String constant = mappingPrism.values.constant() == null ? null : mappingPrism.constant();
String expression = getExpression( mappingPrism, element, messager );
String defaultExpression = getDefaultExpression( mappingPrism, element, messager );
String dateFormat = mappingPrism.values.dateFormat() == null ? null : mappingPrism.dateFormat();
String numberFormat = mappingPrism.values.numberFormat() == null ? null : mappingPrism.numberFormat();
String defaultValue = mappingPrism.values.defaultValue() == null ? null : mappingPrism.defaultValue();
@ -159,6 +173,7 @@ public class Mapping {
source,
constant,
expression,
defaultExpression,
mappingPrism.target(),
defaultValue,
mappingPrism.ignore(),
@ -173,14 +188,15 @@ public class Mapping {
}
@SuppressWarnings("checkstyle:parameternumber")
private Mapping( String sourceName, String constant, String javaExpression, String targetName,
String defaultValue, boolean isIgnored, AnnotationMirror mirror,
private Mapping( String sourceName, String constant, String javaExpression, String defaultJavaExpression,
String targetName, String defaultValue, boolean isIgnored, AnnotationMirror mirror,
AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue,
FormattingParameters formattingParameters, SelectionParameters selectionParameters,
AnnotationValue dependsOnAnnotationValue, List<String> dependsOn ) {
this.sourceName = sourceName;
this.constant = constant;
this.javaExpression = javaExpression;
this.defaultJavaExpression = defaultJavaExpression;
this.targetName = targetName;
this.defaultValue = defaultValue;
this.isIgnored = isIgnored;
@ -197,6 +213,7 @@ public class Mapping {
this.sourceName = mapping.sourceName;
this.constant = mapping.constant;
this.javaExpression = mapping.javaExpression;
this.defaultJavaExpression = mapping.defaultJavaExpression;
this.targetName = Strings.join( targetReference.getElementNames(), "." );
this.defaultValue = mapping.defaultValue;
this.isIgnored = mapping.isIgnored;
@ -215,6 +232,7 @@ public class Mapping {
this.sourceName = Strings.join( sourceReference.getElementNames(), "." );
this.constant = mapping.constant;
this.javaExpression = mapping.javaExpression;
this.defaultJavaExpression = mapping.defaultJavaExpression;
this.targetName = mapping.targetName;
this.defaultValue = mapping.defaultValue;
this.isIgnored = mapping.isIgnored;
@ -248,6 +266,25 @@ public class Mapping {
return javaExpressionMatcher.group( 1 ).trim();
}
private static String getDefaultExpression(MappingPrism mappingPrism, ExecutableElement element,
FormattingMessager messager) {
if ( mappingPrism.defaultExpression().isEmpty() ) {
return null;
}
Matcher javaExpressionMatcher = JAVA_EXPRESSION.matcher( mappingPrism.defaultExpression() );
if ( !javaExpressionMatcher.matches() ) {
messager.printMessage(
element, mappingPrism.mirror, mappingPrism.values.defaultExpression(),
Message.PROPERTYMAPPING_INVALID_DEFAULT_EXPRESSION
);
return null;
}
return javaExpressionMatcher.group( 1 ).trim();
}
private static boolean isEnumType(TypeMirror mirror) {
return mirror.getKind() == TypeKind.DECLARED &&
( (DeclaredType) mirror ).asElement().getKind() == ElementKind.ENUM;
@ -321,6 +358,10 @@ public class Mapping {
return javaExpression;
}
public String getDefaultJavaExpression() {
return defaultJavaExpression;
}
public String getTargetName() {
return targetName;
}
@ -401,6 +442,7 @@ public class Mapping {
sourceName != null ? targetName : null,
null, // constant
null, // expression
null, // defaultExpression
sourceName != null ? sourceName : targetName,
null,
isIgnored,
@ -440,6 +482,7 @@ public class Mapping {
sourceName,
constant,
javaExpression,
defaultJavaExpression,
targetName,
defaultValue,
isIgnored,

View File

@ -54,7 +54,11 @@ public enum Message {
PROPERTYMAPPING_EXPRESSION_AND_CONSTANT_BOTH_DEFINED( "Expression and constant are both defined in @Mapping, either define an expression or a constant." ),
PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_VALUE_BOTH_DEFINED( "Expression and default value are both defined in @Mapping, either define a defaultValue or an expression." ),
PROPERTYMAPPING_CONSTANT_AND_DEFAULT_VALUE_BOTH_DEFINED( "Constant and default value are both defined in @Mapping, either define a defaultValue or a constant." ),
PROPERTYMAPPING_INVALID_EXPRESSION( "Value must be given in the form \"java(<EXPRESSION>)\"." ),
PROPERTYMAPPING_EXPRESSION_AND_DEFAULT_EXPRESSION_BOTH_DEFINED( "Expression and default expression are both defined in @Mapping, either define an expression or a default expression." ),
PROPERTYMAPPING_CONSTANT_AND_DEFAULT_EXPRESSION_BOTH_DEFINED( "Constant and default expression are both defined in @Mapping, either define a constant or a default expression." ),
PROPERTYMAPPING_DEFAULT_VALUE_AND_DEFAULT_EXPRESSION_BOTH_DEFINED( "Default value and default expression are both defined in @Mapping, either define a default value or a default expression." ),
PROPERTYMAPPING_INVALID_EXPRESSION( "Value for expression must be given in the form \"java(<EXPRESSION>)\"." ),
PROPERTYMAPPING_INVALID_DEFAULT_EXPRESSION( "Value for default expression must be given in the form \"java(<EXPRESSION>)\"." ),
PROPERTYMAPPING_INVALID_PARAMETER_NAME( "Method has no parameter named \"%s\"." ),
PROPERTYMAPPING_NO_PROPERTY_IN_PARAMETER( "The type of parameter \"%s\" has no property named \"%s\"." ),
PROPERTYMAPPING_INVALID_PROPERTY_NAME( "No property named \"%s\" exists in source parameter(s). Did you mean \"%s\"?" ),

View File

@ -0,0 +1,39 @@
/**
* Copyright 2012-2017 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.source.defaultExpressions.java;
import java.util.Date;
import java.util.UUID;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
/**
* @author Jeffrey Smyth
*/
@Mapper(imports = { UUID.class, Date.class })
public interface ErroneousDefaultExpressionConstantMapper {
@Mappings({
@Mapping(target = "sourceId", constant = "3", defaultExpression = "java( UUID.randomUUID().toString() )"),
@Mapping(target = "sourceDate", source = "date", defaultExpression = "java( new Date())")
})
Target sourceToTarget(Source s);
}

View File

@ -0,0 +1,39 @@
/**
* Copyright 2012-2017 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.source.defaultExpressions.java;
import java.util.Date;
import java.util.UUID;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
/**
* @author Jeffrey Smyth
*/
@Mapper(imports = { UUID.class, Date.class })
public interface ErroneousDefaultExpressionDefaultValueMapper {
@Mappings({
@Mapping(target = "sourceId", defaultValue = "3", defaultExpression = "java( UUID.randomUUID().toString() )"),
@Mapping(target = "sourceDate", source = "date", defaultExpression = "java( new Date())")
})
Target sourceToTarget(Source s);
}

View File

@ -0,0 +1,40 @@
/**
* Copyright 2012-2017 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.source.defaultExpressions.java;
import java.util.Date;
import java.util.UUID;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
/**
* @author Jeffrey Smyth
*/
@Mapper(imports = { UUID.class, Date.class })
public interface ErroneousDefaultExpressionExpressionMapper {
@Mappings({
@Mapping(target = "sourceId", expression = "java( UUID.randomUUID().toString() )",
defaultExpression = "java( UUID.randomUUID().toString() )"),
@Mapping(target = "sourceDate", source = "date", defaultExpression = "java( new Date())")
})
Target sourceToTarget(Source s);
}

View File

@ -0,0 +1,39 @@
/**
* Copyright 2012-2017 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.source.defaultExpressions.java;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import java.util.Date;
import java.util.UUID;
/**
* @author Jeffrey Smyth
*/
@Mapper(imports = { UUID.class, Date.class })
public interface ErroneousDefaultExpressionMapper {
@Mappings({
@Mapping(target = "sourceId", source = "id", defaultExpression = "UUID.randomUUID().toString()"),
@Mapping(target = "sourceDate", source = "date", defaultExpression = "java( new Date())")
})
Target sourceToTarget(Source s);
}

View File

@ -0,0 +1,142 @@
/**
* Copyright 2012-2017 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.source.defaultExpressions.java;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.compilation.annotation.CompilationResult;
import org.mapstruct.ap.testutil.compilation.annotation.Diagnostic;
import org.mapstruct.ap.testutil.compilation.annotation.ExpectedCompilationOutcome;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
import java.util.Date;
import static org.assertj.core.api.Assertions.assertThat;
/**
* @author Jeffrey Smyth
*/
@RunWith(AnnotationProcessorTestRunner.class)
public class JavaDefaultExpressionTest {
@Test
@WithClasses({ Source.class, Target.class, SourceTargetMapper.class })
public void testJavaDefaultExpressionWithValues() {
Source source = new Source();
source.setId( 123 );
source.setDate( new Date( 0L ) );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getSourceId() ).isEqualTo( "123" );
assertThat( target.getSourceDate() ).isEqualTo( source.getDate() );
}
@Test
@WithClasses({ Source.class, Target.class, SourceTargetMapper.class })
public void testJavaDefaultExpressionWithNoValues() {
Source source = new Source();
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getSourceId() ).isEqualTo( "test" );
assertThat( target.getSourceDate() ).isEqualTo( new Date( 30L ) );
}
@Test
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousDefaultExpressionExpressionMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 39,
messageRegExp = "Expression and default expression are both defined in @Mapping,"
+ " either define an expression or a default expression."
),
@Diagnostic(type = ErroneousDefaultExpressionExpressionMapper.class,
kind = javax.tools.Diagnostic.Kind.WARNING,
line = 39,
messageRegExp = "Unmapped target property: \"sourceId\""
)
}
)
@WithClasses({ Source.class, Target.class, ErroneousDefaultExpressionExpressionMapper.class })
public void testJavaDefaultExpressionExpression() {
}
@Test
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousDefaultExpressionConstantMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 38,
messageRegExp = "Constant and default expression are both defined in @Mapping,"
+ " either define a constant or a default expression."
),
@Diagnostic(type = ErroneousDefaultExpressionConstantMapper.class,
kind = javax.tools.Diagnostic.Kind.WARNING,
line = 38,
messageRegExp = "Unmapped target property: \"sourceId\""
)
}
)
@WithClasses({ Source.class, Target.class, ErroneousDefaultExpressionConstantMapper.class })
public void testJavaDefaultExpressionConstant() {
}
@Test
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousDefaultExpressionDefaultValueMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 38,
messageRegExp = "Default value and default expression are both defined in @Mapping,"
+ " either define a default value or a default expression."
),
@Diagnostic(type = ErroneousDefaultExpressionDefaultValueMapper.class,
kind = javax.tools.Diagnostic.Kind.WARNING,
line = 38,
messageRegExp = "Unmapped target property: \"sourceId\""
)
}
)
@WithClasses({ Source.class, Target.class, ErroneousDefaultExpressionDefaultValueMapper.class })
public void testJavaDefaultExpressionDefaultValue() {
}
@Test
@ExpectedCompilationOutcome(
value = CompilationResult.FAILED,
diagnostics = {
@Diagnostic(type = ErroneousDefaultExpressionMapper.class,
kind = javax.tools.Diagnostic.Kind.ERROR,
line = 35,
messageRegExp = "Value for default expression must be given in the form \"java\\(<EXPRESSION>\\)\""
)
}
)
@WithClasses({ Source.class, Target.class, ErroneousDefaultExpressionMapper.class })
public void testJavaDefaultExpressionInvalidExpression() {
}
}

View File

@ -0,0 +1,50 @@
/**
* Copyright 2012-2017 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.source.defaultExpressions.java;
import java.util.Date;
/**
* @author Jeffrey Smyth
*/
public class Source {
private int id = -1;
private Date date;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public boolean hasId() {
return id != -1;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}

View File

@ -0,0 +1,41 @@
/**
* Copyright 2012-2017 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.source.defaultExpressions.java;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
import java.util.Date;
/**
* @author Jeffrey Smyth
*/
@Mapper(imports = { Date.class })
public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
@Mappings({
@Mapping(target = "sourceId", source = "id", defaultExpression = "java( String.valueOf( \"test\" ) )"),
@Mapping(target = "sourceDate", source = "date", defaultExpression = "java( new Date( 30L ))")
})
Target sourceToTarget(Source s);
}

View File

@ -0,0 +1,46 @@
/**
* Copyright 2012-2017 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.source.defaultExpressions.java;
import java.util.Date;
/**
* @author Jeffrey Smyth
*/
public class Target {
private String sourceId;
private Date sourceDate;
public String getSourceId() {
return sourceId;
}
public void setSourceId(String sourceId) {
this.sourceId = sourceId;
}
public Date getSourceDate() {
return sourceDate;
}
public void setSourceDate(Date sourceDate) {
this.sourceDate = sourceDate;
}
}