diff --git a/core-jdk8/src/main/java/org/mapstruct/Mapping.java b/core-jdk8/src/main/java/org/mapstruct/Mapping.java index 484e7f4fc..d9c716fb2 100644 --- a/core-jdk8/src/main/java/org/mapstruct/Mapping.java +++ b/core-jdk8/src/main/java/org/mapstruct/Mapping.java @@ -42,8 +42,9 @@ public @interface Mapping { *
  • The source name of the configured property as defined by the JavaBeans specification.
  • *
  • When used to map an enum constant, the name of the constant member is to be given<./li>. * - * Either this attribute or {@link #constant()} may be specified for a given mapping, but not both at the same - * time. + * Either this attribute or {@link #constant()} or {@link #expression()} may be specified for a given mapping, + * but not two at the same time. If this attribute is given, the target property must be specified via + * {@link #target()}. * * @return The source name of the configured property or enum constant. */ @@ -70,11 +71,26 @@ 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. *

    - * Either this attribute or {@link #source()} may be specified for a given mapping, but not both at the same time. - * If this attribute is given, the target property must be specified via {@link #target()}. + * Either this attribute or {@link #source()} or {@link #expression()} may be specified for a given mapping, + * but not two at the same time. If this attribute is given, the target property must be specified via + * {@link #target()}. * * @return A constant {@code String} constant specifying the value for the designated target property */ String constant() default ""; + /** + * An expression {@link String} based on which the specified target property is to be set. + * + * The format is determined by a type of expression. For instance: + * {@code expression = "java(new org.example.TimeAndFormat( s.getTime(), s.getFormat() ))")} will insert the java + * expression in the designated {@link #target()} property. + *

    + * Either this attribute or {@link #source()} or {@link #constant()} may be specified for a given mapping, + * but not two at the same time. If this attribute is given, the target property must be specified via + * {@link #target()}. + * + * @return A constant {@code String} constant specifying the value for the designated target property + */ + String expression() default ""; } diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index 656775796..7bd95d7e3 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -40,8 +40,9 @@ public @interface Mapping { *

  • The source name of the configured property as defined by the JavaBeans specification.
  • *
  • When used to map an enum constant, the name of the constant member is to be given<./li>. * - * Either this attribute or {@link #constant()} may be specified for a given mapping, but not both at the same - * time. + * Either this attribute or {@link #constant()} or {@link #expression()} may be specified for a given mapping, + * but not two at the same time. If this attribute is given, the target property must be specified via + * {@link #target()}. * * @return The source name of the configured property or enum constant. */ @@ -68,11 +69,27 @@ 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. *

    - * Either this attribute or {@link #source()} may be specified for a given mapping, but not both at the same time. - * If this attribute is given, the target property must be specified via {@link #target()}. + * Either this attribute or {@link #source()} or {@link #expression()} may be specified for a given mapping, + * but not two at the same time. If this attribute is given, the target property must be specified via + * {@link #target()}. * * @return A constant {@code String} constant specifying the value for the designated target property */ String constant() default ""; + /** + * An expression {@link String} based on which the specified target property is to be set. + * + * The format is determined by a type of expression. For instance: + * {@code expression = "java(new org.example.TimeAndFormat( s.getTime(), s.getFormat() ))")} will insert the java + * expression in the designated {@link #target()} property. + *

    + * Either this attribute or {@link #source()} or {@link #constant()} may be specified for a given mapping, + * but not two at the same time. If this attribute is given, the target property must be specified via + * {@link #target()}. + * + * @return A constant {@code String} constant specifying the value for the designated target property + */ + String expression() default ""; + } diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java b/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java index 0b55374a2..bfa1effb2 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java +++ b/processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java @@ -22,6 +22,8 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.annotation.processing.Messager; import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; @@ -39,10 +41,14 @@ import org.mapstruct.ap.util.AnnotationProcessingException; */ public class Mapping { + private static final Pattern JAVA_EXPRESSION = Pattern.compile( "^java\\((.*)\\)$" ); + private final String sourceName; private final String sourceParameterName; private final String sourcePropertyName; private final String constant; + private final String expression; + private final String javaExpression; private final String targetName; private final String dateFormat; private final AnnotationMirror mirror; @@ -74,10 +80,12 @@ public class Mapping { mappingPrism.values.source() ); - if ( mappingPrism.source().isEmpty() && mappingPrism.constant().isEmpty() ) { + if ( mappingPrism.source().isEmpty() && + mappingPrism.constant().isEmpty() && + mappingPrism.expression().isEmpty() ) { messager.printMessage( Diagnostic.Kind.ERROR, - "Either define a source or a constant in a Mapping", + "Either define a source, a constant or an epression in a Mapping", element ); return null; @@ -85,17 +93,33 @@ public class Mapping { else if ( !mappingPrism.source().isEmpty() && !mappingPrism.constant().isEmpty() ) { messager.printMessage( Diagnostic.Kind.ERROR, - "Source and constant are both defined in Mapping, either define a source or an expression", + "Source and constant are both defined in Mapping, either define a source or a constant", + element + ); + return null; + } + else if ( !mappingPrism.source().isEmpty() && !mappingPrism.expression().isEmpty() ) { + messager.printMessage( + Diagnostic.Kind.ERROR, + "Source and expression are both defined in Mapping, either define a source or an expression", + element + ); + return null; + } + else if ( !mappingPrism.expression().isEmpty() && !mappingPrism.constant().isEmpty() ) { + messager.printMessage( + Diagnostic.Kind.ERROR, + "Expression and constant are both defined in Mapping, either define an expression or a constant", element ); return null; } - return new Mapping( mappingPrism.source(), sourceNameParts != null ? sourceNameParts[0] : null, sourceNameParts != null ? sourceNameParts[1] : mappingPrism.source(), mappingPrism.constant(), + mappingPrism.expression(), mappingPrism.target(), mappingPrism.dateFormat(), mappingPrism.mirror, @@ -124,12 +148,15 @@ public class Mapping { } private Mapping(String sourceName, String sourceParameterName, String sourcePropertyName, String constant, - String targetName, String dateFormat, AnnotationMirror mirror, + String expression, String targetName, String dateFormat, AnnotationMirror mirror, AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue) { this.sourceName = sourceName; this.sourceParameterName = sourceParameterName; this.sourcePropertyName = sourcePropertyName; this.constant = constant; + this.expression = expression; + Matcher javaExpressionMatcher = JAVA_EXPRESSION.matcher( expression ); + this.javaExpression = javaExpressionMatcher.matches() ? javaExpressionMatcher.group( 1 ) : ""; this.targetName = targetName.equals( "" ) ? sourceName : targetName; this.dateFormat = dateFormat; this.mirror = mirror; @@ -169,6 +196,9 @@ public class Mapping { return constant; } + public String getJavaExpression() { + return javaExpression; + } public String getTargetName() { return targetName; @@ -200,6 +230,7 @@ public class Mapping { null, targetName, constant, + expression, sourceName, dateFormat, mirror, diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java index 5ed19ab1b..4e917ced7 100644 --- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java @@ -406,13 +406,30 @@ public class MapperCreationProcessor implements ModelElementProcessor sourceGetters = parameter.getType().getGetters(); - // check constants first - if ( isSourceConstant ) { - return getConstantMapping( - mapperReferences, - methods, - method, - sourceConstant, - targetAccessor, - dateFormat - ); - } - // then iterate over source accessors (assuming the source is a bean) for ( ExecutableElement sourceAccessor : sourceGetters ) { @@ -676,6 +681,7 @@ public class MapperCreationProcessor implements ModelElementProcessor methods, SourceMethod method, String constantExpression, - ExecutableElement targetAcessor, + ExecutableElement targetAccessor, String dateFormat) { // source @@ -851,14 +857,8 @@ public class MapperCreationProcessor implements ModelElementProcessor