From b1f03689d98ab4875480d2ac91ed10319e1ca74f Mon Sep 17 00:00:00 2001 From: sjaakd Date: Tue, 21 Jun 2016 23:55:39 +0200 Subject: [PATCH] #669 rework into sourcepresencecheck SPI and nullvaluecheckstrategy --- copyright.txt | 1 + .../src/main/java/org/mapstruct/Mapper.java | 10 +- .../main/java/org/mapstruct/MapperConfig.java | 10 +- .../org/mapstruct/NullValueCheckStrategy.java | 61 +++++ .../src/main/java/org/mapstruct/Mapping.java | 10 - core/src/main/java/org/mapstruct/Mapping.java | 10 - .../ap/internal/model/BeanMappingMethod.java | 12 +- .../ap/internal/model/PropertyMapping.java | 180 ++++++++++----- .../model/assignment/NullCheckWrapper.java | 10 +- .../assignment/UpdateNullCheckWrapper.java | 10 +- .../ap/internal/model/source/Mapping.java | 31 +-- .../model/source/SourceReference.java | 2 +- .../prism/NullValueCheckStrategy.java} | 17 +- .../ap/internal/services/Services.java | 81 ------- .../ap/internal/util/Executables.java | 10 +- .../ap/internal/util/MapperConfiguration.java | 28 +-- .../mapstruct/ap/internal/util/Services.java | 51 ++++ .../DefaultAccessorNamingStrategy.java | 21 +- .../PresenceCheckAccessorNamingStrategy.java | 53 +++++ .../model/NestedPropertyMappingMethod.ftl | 14 +- .../model/assignment/NullCheckWrapper.ftl | 2 +- .../assignment/UpdateNullCheckWrapper.ftl | 2 +- .../test/presencecheck/PresenceCheckTest.java | 171 -------------- .../presencecheck/SourceTargetMapper.java | 82 ------- .../PresenceCheckTest.java | 76 ++++++ .../RockFestivalMapper.java | 43 ++++ .../RockFestivalMapperConfig.java | 27 +-- .../RockFestivalMapperOveridingConfig.java | 44 ++++ .../RockFestivalMapperWithConfig.java} | 32 ++- .../RockFestivalSource.java} | 24 +- .../RockFestivalTarget.java} | 17 +- .../source/nullvaluecheckstrategy/Stage.java | 53 +++++ .../presencecheck/spi/GoalKeeper.java} | 29 +-- .../presencecheck/spi/PresenceCheckTest.java | 217 ++++++++++++++++++ .../presencecheck/spi/SoccerTeamMapper.java} | 39 ++-- .../presencecheck/spi/SoccerTeamSource.java | 66 ++++++ .../presencecheck/spi/SoccerTeamTarget.java | 52 +++++ .../presencecheck/spi}/Source.java | 77 ++----- .../presencecheck/spi/SourceTargetMapper.java | 59 +++++ .../presencecheck/spi}/Target.java | 39 +--- 40 files changed, 1079 insertions(+), 694 deletions(-) create mode 100644 core-common/src/main/java/org/mapstruct/NullValueCheckStrategy.java rename processor/src/{test/java/org/mapstruct/ap/test/presencecheck/MyObject.java => main/java/org/mapstruct/ap/internal/prism/NullValueCheckStrategy.java} (77%) delete mode 100644 processor/src/main/java/org/mapstruct/ap/internal/services/Services.java create mode 100644 processor/src/main/java/org/mapstruct/ap/internal/util/Services.java rename processor/src/main/java/org/mapstruct/ap/{internal/naming => spi}/DefaultAccessorNamingStrategy.java (83%) create mode 100644 processor/src/main/java/org/mapstruct/ap/spi/PresenceCheckAccessorNamingStrategy.java delete mode 100644 processor/src/test/java/org/mapstruct/ap/test/presencecheck/PresenceCheckTest.java delete mode 100644 processor/src/test/java/org/mapstruct/ap/test/presencecheck/SourceTargetMapper.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/PresenceCheckTest.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapper.java rename core-common/src/main/java/org/mapstruct/SourceValuePresenceCheckStrategy.java => processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperConfig.java (61%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperOveridingConfig.java rename processor/src/test/java/org/mapstruct/ap/test/{presencecheck/TargetWtCheck.java => source/nullvaluecheckstrategy/RockFestivalMapperWithConfig.java} (56%) rename processor/src/test/java/org/mapstruct/ap/test/{presencecheck/CustomMapper.java => source/nullvaluecheckstrategy/RockFestivalSource.java} (64%) rename processor/src/test/java/org/mapstruct/ap/test/{presencecheck/MyLongWrapper.java => source/nullvaluecheckstrategy/RockFestivalTarget.java} (77%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/Stage.java rename processor/src/test/java/org/mapstruct/ap/test/{presencecheck/SourceWtCheck.java => source/presencecheck/spi/GoalKeeper.java} (61%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckTest.java rename processor/src/{main/java/org/mapstruct/ap/internal/prism/SourceValuePresenceCheckStrategy.java => test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamMapper.java} (51%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamSource.java create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamTarget.java rename processor/src/test/java/org/mapstruct/ap/test/{presencecheck => source/presencecheck/spi}/Source.java (64%) create mode 100644 processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SourceTargetMapper.java rename processor/src/test/java/org/mapstruct/ap/test/{presencecheck => source/presencecheck/spi}/Target.java (70%) diff --git a/copyright.txt b/copyright.txt index 4f1c07002..46dc97558 100644 --- a/copyright.txt +++ b/copyright.txt @@ -17,6 +17,7 @@ Remko Plantenga Samuel Wright Sebastian Hasait Sjaak Derksen +Sean Huang Timo Eckhardt Tomek Gubala Vincent Alexander Beelte diff --git a/core-common/src/main/java/org/mapstruct/Mapper.java b/core-common/src/main/java/org/mapstruct/Mapper.java index d90636a0e..bbc3f669f 100644 --- a/core-common/src/main/java/org/mapstruct/Mapper.java +++ b/core-common/src/main/java/org/mapstruct/Mapper.java @@ -18,14 +18,13 @@ */ package org.mapstruct; -import static org.mapstruct.SourceValuePresenceCheckStrategy.IS_NULL_INLINE; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.mapstruct.factory.Mappers; +import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION; /** * Marks an interface or abstract class as a mapper and activates the generation of a implementation of that type via @@ -152,10 +151,11 @@ public @interface Mapper { MappingInheritanceStrategy mappingInheritanceStrategy() default MappingInheritanceStrategy.EXPLICIT; /** - * Decide how to do presence check, such as checking null or calling hasX method, before mapping. + * Determines when to include a null check on the source property value of a bean mapping. + * * Can be overridden by the one on {@link MapperConfig} or {@link Mapping}. * - * @return strategy about how to do null or presence check + * @return strategy how to do null checking */ - SourceValuePresenceCheckStrategy sourceValuePresenceCheckStrategy() default IS_NULL_INLINE; + NullValueCheckStrategy nullValueCheckStrategy() default ON_IMPLICIT_CONVERSION; } diff --git a/core-common/src/main/java/org/mapstruct/MapperConfig.java b/core-common/src/main/java/org/mapstruct/MapperConfig.java index 96e2c93bb..08e710df7 100644 --- a/core-common/src/main/java/org/mapstruct/MapperConfig.java +++ b/core-common/src/main/java/org/mapstruct/MapperConfig.java @@ -18,14 +18,13 @@ */ package org.mapstruct; -import static org.mapstruct.SourceValuePresenceCheckStrategy.IS_NULL_INLINE; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; import org.mapstruct.factory.Mappers; +import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION; /** * Marks a class or interface as configuration source for generated mappers. This allows to share common configurations @@ -139,10 +138,11 @@ public @interface MapperConfig { default MappingInheritanceStrategy.EXPLICIT; /** - * Decide how to do presence check, such as checking null or calling hasXXX method, before mapping. + * Determines when to include a null check on the source property value of a bean mapping. + * * Can be overridden by the one on {@link Mapper} or {@link Mapping}. * - * @return strategy about how to do null or presence check + * @return strategy how to do null checking */ - SourceValuePresenceCheckStrategy sourceValuePresenceCheckStrategy() default IS_NULL_INLINE; + NullValueCheckStrategy nullValueCheckStrategy() default ON_IMPLICIT_CONVERSION; } diff --git a/core-common/src/main/java/org/mapstruct/NullValueCheckStrategy.java b/core-common/src/main/java/org/mapstruct/NullValueCheckStrategy.java new file mode 100644 index 000000000..9f698633c --- /dev/null +++ b/core-common/src/main/java/org/mapstruct/NullValueCheckStrategy.java @@ -0,0 +1,61 @@ +/** + * Copyright 2012-2016 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; + +/** + * Strategy for dealing with null source values. + * + * Note: This strategy is not in effect when the a specific source presence check method is defined + * in the service provider interface (SPI). + * + * @author Sean Huang + */ +public enum NullValueCheckStrategy { + + /** + * This option includes a null check. When: + *

+ *

    + *
  1. a source value is directly assigned to a target
  2. + *
  3. a source value assigned to a target by calling a type conversion on the target first
  4. + *
+ *

+ * NOTE: mapping methods (generated or hand written) are excluded from this null check. They are intended to + * handle a null source value as 'valid' input. + * + *//** + * This option includes a null check. When: + *

+ *

    + *
  1. a source value is directly assigned to a target
  2. + *
  3. a source value assigned to a target by calling a type conversion on the target first
  4. + *
+ *

+ * NOTE: mapping methods (generated or hand written) are excluded from this null check. They are intended to + * handle a null source value as 'valid' input. + * + */ + ON_IMPLICIT_CONVERSION, + + /** + * This option always includes a null check. + */ + ALLWAYS, + +} diff --git a/core-jdk8/src/main/java/org/mapstruct/Mapping.java b/core-jdk8/src/main/java/org/mapstruct/Mapping.java index e02146ace..ff217389c 100644 --- a/core-jdk8/src/main/java/org/mapstruct/Mapping.java +++ b/core-jdk8/src/main/java/org/mapstruct/Mapping.java @@ -18,8 +18,6 @@ */ package org.mapstruct; -import static org.mapstruct.SourceValuePresenceCheckStrategy.IS_NULL; - import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Repeatable; @@ -193,12 +191,4 @@ public @interface Mapping { * @return Default value to set in case the source property is {@code null}. */ String defaultValue() default ""; - - /** - * Decide whether we should check null or hasX method before mapping. - * The value on {@link Mapper} can override this one. - * - * @return strategy about how to do null or has value check - */ - SourceValuePresenceCheckStrategy sourceValuePresenceCheckStrategy() default IS_NULL; } diff --git a/core/src/main/java/org/mapstruct/Mapping.java b/core/src/main/java/org/mapstruct/Mapping.java index 430d5d345..4a902fe70 100644 --- a/core/src/main/java/org/mapstruct/Mapping.java +++ b/core/src/main/java/org/mapstruct/Mapping.java @@ -18,8 +18,6 @@ */ package org.mapstruct; -import static org.mapstruct.SourceValuePresenceCheckStrategy.IS_NULL_INLINE; - import java.lang.annotation.Annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; @@ -192,12 +190,4 @@ public @interface Mapping { * @return Default value to set in case the source property is {@code null}. */ String defaultValue() default ""; - - /** - * Decide how to do presence check, such as checking null or calling hasXXX method, before mapping. - * If it is set to default, it can be overridden by the one on {@link MapperConfig} or {@link Mapper}. - * - * @return strategy about how to do null or presence check - */ - SourceValuePresenceCheckStrategy sourceValuePresenceCheckStrategy() default IS_NULL_INLINE; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java index 3f7efde85..70298270b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/BeanMappingMethod.java @@ -370,7 +370,7 @@ public class BeanMappingMethod extends MappingMethod { while ( targetPropertiesIterator.hasNext() ) { Entry targetProperty = targetPropertiesIterator.next(); - String propertyName = targetProperty.getKey(); + String targetPropertyName = targetProperty.getKey(); PropertyMapping propertyMapping = null; @@ -387,10 +387,10 @@ public class BeanMappingMethod extends MappingMethod { PropertyMapping newPropertyMapping = null; ExecutableElement sourceReadAccessor = - sourceParameter.getType().getPropertyReadAccessors().get( propertyName ); + sourceParameter.getType().getPropertyReadAccessors().get( targetPropertyName ); ExecutableElement sourcePresenceChecker = - sourceParameter.getType().getPropertyPresenceCheckers().get( propertyName ); + sourceParameter.getType().getPropertyPresenceCheckers().get( targetPropertyName ); if ( sourceReadAccessor != null ) { Mapping mapping = method.getSingleMappingByTargetPropertyName( targetProperty.getKey() ); @@ -408,8 +408,8 @@ public class BeanMappingMethod extends MappingMethod { .mappingContext( ctx ) .sourceMethod( method ) .targetWriteAccessor( targetProperty.getValue() ) - .targetReadAccessor( getTargetPropertyReadAccessor( propertyName ) ) - .targetPropertyName( propertyName ) + .targetReadAccessor( getTargetPropertyReadAccessor( targetPropertyName ) ) + .targetPropertyName( targetPropertyName ) .sourceReference( sourceRef ) .formattingParameters( mapping != null ? mapping.getFormattingParameters() : null ) .selectionParameters( mapping != null ? mapping.getSelectionParameters() : null ) @@ -426,7 +426,7 @@ public class BeanMappingMethod extends MappingMethod { ctx.getMessager().printMessage( method.getExecutable(), Message.BEANMAPPING_SEVERAL_POSSIBLE_SOURCES, - propertyName + targetPropertyName ); break; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java index e0bc63626..b13753571 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/PropertyMapping.java @@ -23,6 +23,7 @@ import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentTy import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.MAPPED_TYPE_CONVERTED; import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.TYPE_CONVERTED; import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.TYPE_CONVERTED_MAPPED; +import static org.mapstruct.ap.internal.prism.NullValueCheckStrategy.ALLWAYS; import java.util.Arrays; import java.util.Collections; @@ -52,6 +53,7 @@ import org.mapstruct.ap.internal.model.source.SelectionParameters; import org.mapstruct.ap.internal.model.source.SourceMethod; import org.mapstruct.ap.internal.model.source.SourceReference; import org.mapstruct.ap.internal.model.source.SourceReference.PropertyEntry; +import static org.mapstruct.ap.internal.util.Collections.first; import org.mapstruct.ap.internal.util.Executables; import org.mapstruct.ap.internal.util.MapperConfiguration; import org.mapstruct.ap.internal.util.Message; @@ -235,23 +237,14 @@ public class PropertyMapping extends ModelElement { if ( assignment != null ) { if ( targetType.isCollectionOrMapType() ) { - assignment = assignCollection( targetType, targetWriteAccessorType, assignment ); + assignment = assignToCollection( targetType, targetWriteAccessorType, assignment ); } else if ( targetType.isArrayType() && sourceType.isArrayType() && assignment.getType() == DIRECT ) { - Type arrayType = ctx.getTypeFactory().getType( Arrays.class ); - assignment = new ArrayCopyWrapper( - assignment, - targetPropertyName, - arrayType, - targetType, - existingVariableNames - ); - assignment = new NullCheckWrapper( assignment ); + assignment = assignToArray( targetType, assignment ); } else { - assignment = assignObject( sourceType, targetType, targetWriteAccessorType, assignment ); + assignment = assignToPlain( sourceType, targetType, targetWriteAccessorType, assignment ); } - } else { ctx.getMessager().printMessage( @@ -278,7 +271,9 @@ public class PropertyMapping extends ModelElement { } private Assignment getDefaultValueAssignment() { - if ( defaultValue != null && !getSourceType().isPrimitive() ) { + if ( defaultValue != null + && ( !getSourceType().isPrimitive() || getSourcePresenceCheckerRef() != null) ) { + // cannot check on null source if source is primitive unless it has a presenche checker PropertyMapping build = new ConstantMappingBuilder() .constantExpression( '"' + defaultValue + '"' ) .formattingParameters( formattingParameters ) @@ -296,63 +291,98 @@ public class PropertyMapping extends ModelElement { return null; } - private Assignment assignObject(Type sourceType, Type targetType, TargetWriteAccessorType targetAccessorType, - Assignment rhs) { + private Assignment assignToPlain(Type sourceType, Type targetType, TargetWriteAccessorType targetAccessorType, + Assignment rightHandSide) { - Assignment result = rhs; + Assignment result; if ( targetAccessorType == TargetWriteAccessorType.SETTER ) { - if ( result.isUpdateMethod() ) { - if ( targetReadAccessor == null ) { - ctx.getMessager().printMessage( method.getExecutable(), - Message.PROPERTYMAPPING_NO_READ_ACCESSOR_FOR_TARGET_TYPE, - targetPropertyName ); - } - Assignment factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, targetType, null ); - result = new UpdateWrapper( result, method.getThrownTypes(), factoryMethod, - targetType ); - } - else { - result = new SetterWrapper( result, method.getThrownTypes() ); - } - if ( !sourceType.isPrimitive() - && !sourceReference.getPropertyEntries().isEmpty() ) { // parameter null taken care of by beanmapper - - if ( result.isUpdateMethod() ) { - result = new UpdateNullCheckWrapper( result ); - } - else if ( result.getType() == TYPE_CONVERTED - || result.getType() == TYPE_CONVERTED_MAPPED - || result.getType() == MAPPED_TYPE_CONVERTED - || ( result.getType() == DIRECT && targetType.isPrimitive() ) ) { - // for primitive types null check is not possible at all, but a conversion needs - // a null check. - result = new NullCheckWrapper( result ); - } - } + result = assignToPlainViaSetter( sourceType, targetType, rightHandSide ); } else { - // TargetAccessorType must be ADDER - if ( getSourceType().isCollectionType() ) { - result = new AdderWrapper( - result, - method.getThrownTypes(), - getSourceRef(), - sourceType - ); - result = new NullCheckWrapper( result ); - } - else { - // Possibly adding null to a target collection. So should be surrounded by an null check. - result = new SetterWrapper( result, method.getThrownTypes() ); - result = new NullCheckWrapper( result ); - } + result = assignToPlainViaAdder( sourceType, rightHandSide ); } return result; } - private Assignment assignCollection(Type targetType, + private Assignment assignToPlainViaSetter(Type sourceType, Type targetType, Assignment rightHandSide) { + + Assignment result; + + if ( rightHandSide.isUpdateMethod() ) { + if ( targetReadAccessor == null ) { + ctx.getMessager().printMessage( method.getExecutable(), + Message.PROPERTYMAPPING_NO_READ_ACCESSOR_FOR_TARGET_TYPE, + targetPropertyName ); + } + Assignment factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, targetType, null ); + result = new UpdateWrapper( rightHandSide, method.getThrownTypes(), factoryMethod, + targetType ); + } + else { + result = new SetterWrapper( rightHandSide, method.getThrownTypes() ); + } + + // if the sourceReference is the bean mapping method parameter itself, no null check is required + // since this is handled by the BeanMapping + if ( sourceReference.getPropertyEntries().isEmpty() ) { + return result; + } + + // add a null / presence checked when required + if ( sourceType.isPrimitive() ) { + if ( getSourcePresenceCheckerRef() != null ) { + result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); + } + } + else { + + if ( result.isUpdateMethod() ) { + result = new UpdateNullCheckWrapper( result, getSourcePresenceCheckerRef() ); + } + else if ( getSourcePresenceCheckerRef() != null ) { + result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); + } + else if ( ALLWAYS.equals( method.getMapperConfiguration().getNullValueCheckStrategy() ) ) { + result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); + } + else if ( result.getType() == TYPE_CONVERTED + || result.getType() == TYPE_CONVERTED_MAPPED + || result.getType() == MAPPED_TYPE_CONVERTED + || (result.getType() == DIRECT && targetType.isPrimitive()) ) { + // for primitive types null check is not possible at all, but a conversion needs + // a null check. + result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); + } + } + + return result; + } + + + private Assignment assignToPlainViaAdder(Type sourceType, Assignment rightHandSide) { + + Assignment result = rightHandSide; + + if ( getSourceType().isCollectionType() ) { + result = new AdderWrapper( + result, + method.getThrownTypes(), + getSourceRef(), + sourceType + ); + result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); + } + else { + // Possibly adding null to a target collection. So should be surrounded by an null check. + result = new SetterWrapper( result, method.getThrownTypes() ); + result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); + } + return result; + } + + private Assignment assignToCollection(Type targetType, TargetWriteAccessorType targetAccessorType, Assignment rhs) { @@ -419,15 +449,28 @@ public class PropertyMapping extends ModelElement { // for mapping methods (builtin / custom), the mapping method is responsible for the // null check. Typeconversions do not apply to collections and maps. if ( result.getType() == DIRECT ) { - result = new NullCheckWrapper( result ); + result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() ); } else if ( result.getType() == MAPPED && result.isUpdateMethod() ) { - result = new UpdateNullCheckWrapper( result ); + result = new UpdateNullCheckWrapper( result, getSourcePresenceCheckerRef() ); } return result; } + private Assignment assignToArray(Type targetType, Assignment rightHandSide) { + + Type arrayType = ctx.getTypeFactory().getType( Arrays.class ); + Assignment assignment = new ArrayCopyWrapper( + rightHandSide, + targetPropertyName, + arrayType, + targetType, + existingVariableNames + ); + return new NullCheckWrapper( assignment, getSourcePresenceCheckerRef() ); + } + private Type getSourceType() { Parameter sourceParam = sourceReference.getParameter(); @@ -514,6 +557,19 @@ public class PropertyMapping extends ModelElement { } } + private String getSourcePresenceCheckerRef() { + String sourcePresenceChecker = null; + if ( !sourceReference.getPropertyEntries().isEmpty() ) { + Parameter sourceParam = sourceReference.getParameter(); + PropertyEntry propertyEntry = first( sourceReference.getPropertyEntries() ); + if ( propertyEntry.getPresenceChecker() != null ) { + sourcePresenceChecker = sourceParam.getName() + + "." + propertyEntry.getPresenceChecker().getSimpleName() + "()"; + } + } + return sourcePresenceChecker; + } + private Assignment forgeMapOrIterableMapping(Type sourceType, Type targetType, String sourceReference, ExecutableElement element) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/NullCheckWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/NullCheckWrapper.java index 51a667f4a..003877892 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/NullCheckWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/NullCheckWrapper.java @@ -25,7 +25,15 @@ package org.mapstruct.ap.internal.model.assignment; */ public class NullCheckWrapper extends AssignmentWrapper { - public NullCheckWrapper( Assignment decoratedAssignment ) { + private final String sourcePresenceChecker; + + + public NullCheckWrapper( Assignment decoratedAssignment, String sourcePresenceChecker ) { super( decoratedAssignment ); + this.sourcePresenceChecker = sourcePresenceChecker; + } + + public String getSourcePresenceChecker() { + return sourcePresenceChecker; } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateNullCheckWrapper.java b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateNullCheckWrapper.java index 491d7ee7a..20ab7ec75 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateNullCheckWrapper.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/assignment/UpdateNullCheckWrapper.java @@ -26,7 +26,15 @@ package org.mapstruct.ap.internal.model.assignment; */ public class UpdateNullCheckWrapper extends AssignmentWrapper { - public UpdateNullCheckWrapper( Assignment decoratedAssignment ) { + private final String sourcePresenceChecker; + + public UpdateNullCheckWrapper( Assignment decoratedAssignment, String sourcePresenceChecker ) { super( decoratedAssignment ); + this.sourcePresenceChecker = sourcePresenceChecker; } + + public String getSourcePresenceChecker() { + return sourcePresenceChecker; + } + } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java index 7f15a6241..471f04346 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/Mapping.java @@ -38,7 +38,6 @@ import org.mapstruct.ap.internal.model.common.TypeFactory; import org.mapstruct.ap.internal.prism.CollectionMappingStrategyPrism; import org.mapstruct.ap.internal.prism.MappingPrism; import org.mapstruct.ap.internal.prism.MappingsPrism; -import org.mapstruct.ap.internal.prism.SourceValuePresenceCheckStrategy; import org.mapstruct.ap.internal.util.FormattingMessager; import org.mapstruct.ap.internal.util.Message; @@ -62,9 +61,6 @@ public class Mapping { private final boolean isIgnored; private final List dependsOn; - private final SourceValuePresenceCheckStrategy valuePresenceCheckStrategy; - private final boolean isSetValuePresenceCheckStrategy; - private final AnnotationMirror mirror; private final AnnotationValue sourceAnnotationValue; private final AnnotationValue targetAnnotationValue; @@ -142,7 +138,6 @@ public class Mapping { List dependsOn = mappingPrism.dependsOn() != null ? mappingPrism.dependsOn() : Collections.emptyList(); - boolean isSetValuePresenceCheckStrategy = mappingPrism.values.sourceValuePresenceCheckStrategy() != null; FormattingParameters formattingParam = new FormattingParameters( dateFormat, numberFormat ); SelectionParameters selectionParams = new SelectionParameters( @@ -163,9 +158,7 @@ public class Mapping { formattingParam, selectionParams, mappingPrism.values.dependsOn(), - dependsOn, - SourceValuePresenceCheckStrategy.valueOf( mappingPrism.sourceValuePresenceCheckStrategy() ), - isSetValuePresenceCheckStrategy + dependsOn ); } @@ -174,9 +167,7 @@ public class Mapping { String defaultValue, boolean isIgnored, AnnotationMirror mirror, AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue, FormattingParameters formattingParameters, SelectionParameters selectionParameters, - AnnotationValue dependsOnAnnotationValue, List dependsOn, - SourceValuePresenceCheckStrategy valuePresenceCheckStrategy, - boolean isSetValuePresenceCheckStrategy ) { + AnnotationValue dependsOnAnnotationValue, List dependsOn ) { this.sourceName = sourceName; this.constant = constant; this.javaExpression = javaExpression; @@ -190,8 +181,6 @@ public class Mapping { this.selectionParameters = selectionParameters; this.dependsOnAnnotationValue = dependsOnAnnotationValue; this.dependsOn = dependsOn; - this.valuePresenceCheckStrategy = valuePresenceCheckStrategy; - this.isSetValuePresenceCheckStrategy = isSetValuePresenceCheckStrategy; } private static String getExpression(MappingPrism mappingPrism, ExecutableElement element, @@ -292,14 +281,6 @@ public class Mapping { return dependsOn; } - public SourceValuePresenceCheckStrategy sourceValuePresenceCheckStrategy() { - return valuePresenceCheckStrategy; - } - - public boolean isSetSourceValuePresenceCheckStrategy() { - return isSetValuePresenceCheckStrategy; - } - private boolean hasPropertyInReverseMethod(String name, SourceMethod method) { CollectionMappingStrategyPrism cms = method.getMapperConfiguration().getCollectionMappingStrategy(); return method.getResultType().getPropertyWriteAccessors( cms ).containsKey( name ); @@ -348,9 +329,7 @@ public class Mapping { formattingParameters, selectionParameters, dependsOnAnnotationValue, - Collections.emptyList(), - valuePresenceCheckStrategy, - isSetValuePresenceCheckStrategy + Collections.emptyList() ); reverse.init( method, messager, typeFactory ); @@ -377,9 +356,7 @@ public class Mapping { formattingParameters, selectionParameters, dependsOnAnnotationValue, - dependsOn, - valuePresenceCheckStrategy, - isSetValuePresenceCheckStrategy + dependsOn ); if ( sourceReference != null ) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceReference.java b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceReference.java index eb0a9597c..74aa42f4d 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceReference.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/model/source/SourceReference.java @@ -283,7 +283,7 @@ public class SourceReference { private final Type type; public PropertyEntry(String name, ExecutableElement readAccessor, - ExecutableElement presenceChecker, Type type) { + ExecutableElement presenceChecker, Type type) { this.name = name; this.accessor = readAccessor; this.presenceChecker = presenceChecker; diff --git a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/MyObject.java b/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueCheckStrategy.java similarity index 77% rename from processor/src/test/java/org/mapstruct/ap/test/presencecheck/MyObject.java rename to processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueCheckStrategy.java index 1f035b671..b9917b8f8 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/MyObject.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/prism/NullValueCheckStrategy.java @@ -16,19 +16,16 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mapstruct.ap.test.presencecheck; +package org.mapstruct.ap.internal.prism; + /** + * Prism for the enum {@link org.mapstruct.SourceValuePresenceCheckStrategy} + * * @author Sean Huang */ -public class MyObject { - @Override - public boolean equals(Object object) { - return this == object; - } +public enum NullValueCheckStrategy { - @Override - public int hashCode() { - return super.hashCode(); - } + ON_IMPLICIT_CONVERSION, + ALLWAYS; } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/services/Services.java b/processor/src/main/java/org/mapstruct/ap/internal/services/Services.java deleted file mode 100644 index d4552e6ea..000000000 --- a/processor/src/main/java/org/mapstruct/ap/internal/services/Services.java +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Copyright 2012-2016 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.internal.services; - -import java.util.ServiceLoader; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; - -/** - * A simple locator for SPI implementations. - * - * @author Christian Schuster - */ -public class Services { - - private static final ConcurrentMap, Object> SERVICES = new ConcurrentHashMap, Object>(); - - private Services() { - } - - public static T get(Class serviceType, T defaultValue) { - @SuppressWarnings("unchecked") - T service = (T) SERVICES.get( serviceType ); - - if ( service == null ) { - service = loadAndCache( serviceType, defaultValue ); - } - - return service; - } - - private static T loadAndCache(Class serviceType, T defaultValue) { - T service = find( serviceType ); - if ( service == null ) { - service = defaultValue; - } - - @SuppressWarnings("unchecked") - T cached = (T) SERVICES.putIfAbsent( serviceType, service ); - if ( cached != null ) { - service = (T) cached; - } - - return service; - } - - private static T find(Class spi) { - T matchingImplementation = null; - - for ( T implementation : ServiceLoader.load( spi, spi.getClassLoader() ) ) { - if ( matchingImplementation == null ) { - matchingImplementation = implementation; - } - else { - throw new IllegalStateException( - "Multiple implementations have been found for the service provider interface " - + spi.getCanonicalName() + ": " + matchingImplementation.getClass().getCanonicalName() + ", " - + implementation.getClass().getCanonicalName() + "." - ); - } - } - - return matchingImplementation; - } -} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java index 14a5d8e5d..ed0ac11f1 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Executables.java @@ -35,11 +35,10 @@ import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; import javax.lang.model.util.Elements; -import org.mapstruct.ap.internal.naming.DefaultAccessorNamingStrategy; import org.mapstruct.ap.internal.prism.AfterMappingPrism; import org.mapstruct.ap.internal.prism.BeforeMappingPrism; -import org.mapstruct.ap.internal.services.Services; import org.mapstruct.ap.spi.AccessorNamingStrategy; +import org.mapstruct.ap.spi.DefaultAccessorNamingStrategy; import org.mapstruct.ap.spi.MethodType; /** @@ -63,8 +62,7 @@ public class Executables { } private static final AccessorNamingStrategy ACCESSOR_NAMING_STRATEGY = Services.get( - AccessorNamingStrategy.class, - new DefaultAccessorNamingStrategy() + AccessorNamingStrategy.class, new DefaultAccessorNamingStrategy() ); private Executables() { @@ -98,8 +96,8 @@ public class Executables { return method.getModifiers().contains( Modifier.PUBLIC ); } - public static String getPropertyName(ExecutableElement getterOrHasserOrSetterMethod) { - return ACCESSOR_NAMING_STRATEGY.getPropertyName( getterOrHasserOrSetterMethod ); + public static String getPropertyName(ExecutableElement getterOrPresenceCheckerOrSetterMethod) { + return ACCESSOR_NAMING_STRATEGY.getPropertyName( getterOrPresenceCheckerOrSetterMethod ); } public static boolean isDefaultMethod(ExecutableElement method) { diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java b/processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java index e4420b02a..5bf917a98 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/MapperConfiguration.java @@ -32,7 +32,7 @@ import org.mapstruct.ap.internal.prism.MapperConfigPrism; import org.mapstruct.ap.internal.prism.MapperPrism; import org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism; import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; -import org.mapstruct.ap.internal.prism.SourceValuePresenceCheckStrategy; +import org.mapstruct.ap.internal.prism.NullValueCheckStrategy; /** * Provides an aggregated view to the settings given via {@link org.mapstruct.Mapper} and @@ -136,6 +136,15 @@ public class MapperConfiguration { } } + public NullValueCheckStrategy getNullValueCheckStrategy() { + if ( mapperConfigPrism != null && mapperPrism.values.nullValueCheckStrategy() == null ) { + return NullValueCheckStrategy.valueOf( mapperConfigPrism.nullValueCheckStrategy() ); + } + else { + return NullValueCheckStrategy.valueOf( mapperPrism.nullValueCheckStrategy() ); + } + } + public boolean isMapToDefault(NullValueMappingStrategyPrism mapNullToDefault) { // check on method level @@ -185,21 +194,4 @@ public class MapperConfiguration { return mapperPrism.mirror; } - public SourceValuePresenceCheckStrategy sourceValuePresenceCheckStrategy() { - if ( mapperConfigPrism != null && mapperPrism.values.sourceValuePresenceCheckStrategy() == null ) { - return SourceValuePresenceCheckStrategy.valueOf( mapperConfigPrism.sourceValuePresenceCheckStrategy() ); - } - else { - return SourceValuePresenceCheckStrategy.valueOf( mapperPrism.sourceValuePresenceCheckStrategy() ); - } - } - - public boolean isSetSourceValuePresenceCheckStrategy() { - if ( mapperConfigPrism != null && mapperPrism.values.sourceValuePresenceCheckStrategy() == null ) { - return mapperConfigPrism.values.sourceValuePresenceCheckStrategy() != null; - } - else { - return mapperPrism.values.sourceValuePresenceCheckStrategy() != null; - } - } } diff --git a/processor/src/main/java/org/mapstruct/ap/internal/util/Services.java b/processor/src/main/java/org/mapstruct/ap/internal/util/Services.java new file mode 100644 index 000000000..cc3c98c72 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/internal/util/Services.java @@ -0,0 +1,51 @@ +/** + * Copyright 2012-2016 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.internal.util; + +import java.util.Iterator; +import java.util.ServiceLoader; + +/** + * A simple locator for SPI implementations. + * + * @author Christian Schuster + */ +public class Services { + + private Services() { + } + + public static T get(Class serviceType, T defaultValue) { + + Iterator services = ServiceLoader.load( serviceType, Services.class.getClassLoader() ).iterator(); + + T result; + if ( services.hasNext() ) { + result = services.next(); + } + else { + result = defaultValue; + } + if ( services.hasNext() ) { + throw new IllegalStateException( + "Multiple implementations have been found for the service provider interface" ); + } + return result; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/naming/DefaultAccessorNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java similarity index 83% rename from processor/src/main/java/org/mapstruct/ap/internal/naming/DefaultAccessorNamingStrategy.java rename to processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java index c04e703da..c587af52b 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/naming/DefaultAccessorNamingStrategy.java +++ b/processor/src/main/java/org/mapstruct/ap/spi/DefaultAccessorNamingStrategy.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mapstruct.ap.internal.naming; +package org.mapstruct.ap.spi; import java.beans.Introspector; @@ -28,8 +28,6 @@ import javax.lang.model.type.TypeMirror; import javax.lang.model.util.SimpleElementVisitor6; import javax.lang.model.util.SimpleTypeVisitor6; -import org.mapstruct.ap.spi.AccessorNamingStrategy; -import org.mapstruct.ap.spi.MethodType; /** * The default JavaBeans-compliant implementation of the {@link AccessorNamingStrategy} service provider interface. @@ -43,9 +41,6 @@ public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy { if ( isGetterMethod( method ) ) { return MethodType.GETTER; } - else if ( isPresenceCheckMethod( method ) ) { - return MethodType.PRESENCE_CHECKER; - } else if ( isSetterMethod( method ) ) { return MethodType.SETTER; } @@ -70,14 +65,6 @@ public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy { return isNonBooleanGetterName || ( isBooleanGetterName && returnTypeIsBoolean ); } - private boolean isPresenceCheckMethod(ExecutableElement method) { - String methodName = method.getSimpleName().toString(); - - return methodName.startsWith( "has" ) && methodName.length() > 3 && - ( method.getReturnType().getKind() == TypeKind.BOOLEAN || - "java.lang.Boolean".equals( getQualifiedName( method.getReturnType() ) ) ); - } - public boolean isSetterMethod(ExecutableElement method) { String methodName = method.getSimpleName().toString(); @@ -92,8 +79,8 @@ public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy { @Override - public String getPropertyName(ExecutableElement getterOrHasserOrSetterMethod) { - String methodName = getterOrHasserOrSetterMethod.getSimpleName().toString(); + public String getPropertyName(ExecutableElement getterOrSetterMethod) { + String methodName = getterOrSetterMethod.getSimpleName().toString(); return Introspector.decapitalize( methodName.substring( methodName.startsWith( "is" ) ? 2 : 3 ) ); } @@ -108,7 +95,7 @@ public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy { return "get" + property.substring( 0, 1 ).toUpperCase() + property.substring( 1 ); } - private static String getQualifiedName(TypeMirror type) { + protected static String getQualifiedName(TypeMirror type) { DeclaredType declaredType = type.accept( new SimpleTypeVisitor6() { @Override diff --git a/processor/src/main/java/org/mapstruct/ap/spi/PresenceCheckAccessorNamingStrategy.java b/processor/src/main/java/org/mapstruct/ap/spi/PresenceCheckAccessorNamingStrategy.java new file mode 100644 index 000000000..c533292f9 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/spi/PresenceCheckAccessorNamingStrategy.java @@ -0,0 +1,53 @@ +/** + * Copyright 2012-2016 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.spi; + + +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.type.TypeKind; + +/** + * The default JavaBeans-compliant implementation of the {@link AccessorNamingStrategy} service provider interface. + * + * @author Sjaak Derksen + */ +public class PresenceCheckAccessorNamingStrategy + extends DefaultAccessorNamingStrategy + implements AccessorNamingStrategy { + + @Override + public MethodType getMethodType(ExecutableElement method) { + if ( isPresenceCheckMethod( method ) ) { + return MethodType.PRESENCE_CHECKER; + } + else { + return super.getMethodType( method ); + } + } + + + private boolean isPresenceCheckMethod(ExecutableElement method) { + String methodName = method.getSimpleName().toString(); + + return methodName.startsWith( "has" ) && methodName.length() > 3 && + ( method.getReturnType().getKind() == TypeKind.BOOLEAN || + "java.lang.Boolean".equals( getQualifiedName( method.getReturnType() ) ) ); + } + +} diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl index 96e3bfcdc..675b5533f 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/NestedPropertyMappingMethod.ftl @@ -23,15 +23,23 @@ if ( ${sourceParameter.name} == null ) { return ${returnType.null}; } - <#list propertyEntries as entry> - <@includeModel object=entry.type/> ${entry.name} = <#if entry_index == 0>${sourceParameter.name}.${entry.accessorName}()<#else>${propertyEntries[entry_index-1].name}.${entry.accessorName}(); +<#list propertyEntries as entry> + <#if entry.presenceChecker?? > + if ( !<@localVarName index=entry_index/>.${entry.presenceChecker.simpleName}() ) { + return ${returnType.null}; + } + + <@includeModel object=entry.type/> ${entry.name} = <@localVarName index=entry_index/>.${entry.accessorName}(); + <#if !entry.presenceChecker?? > <#if !entry.type.primitive> if ( ${entry.name} == null ) { return ${returnType.null}; } + <#if !entry_has_next> return ${entry.name}; - + } +<#macro localVarName index><#if index == 0>${sourceParameter.name}<#else>${propertyEntries[index-1].name} \ No newline at end of file diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/NullCheckWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/NullCheckWrapper.ftl index 18336e6ec..c92d8eb29 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/NullCheckWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/NullCheckWrapper.ftl @@ -18,7 +18,7 @@ limitations under the License. --> -if ( ${sourceReference} != null ) { +if ( <#if sourcePresenceChecker?? >${sourcePresenceChecker}<#else>${sourceReference} != null ) { <@includeModel object=assignment targetBeanName=ext.targetBeanName existingInstanceMapping=ext.existingInstanceMapping diff --git a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateNullCheckWrapper.ftl b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateNullCheckWrapper.ftl index fbd9ad604..067c525e5 100644 --- a/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateNullCheckWrapper.ftl +++ b/processor/src/main/resources/org/mapstruct/ap/internal/model/assignment/UpdateNullCheckWrapper.ftl @@ -18,7 +18,7 @@ limitations under the License. --> -if ( ${sourceReference} != null ) { +if ( <#if sourcePresenceChecker?? >${sourcePresenceChecker}<#else>${sourceReference} != null ) { <@includeModel object=assignment targetBeanName=ext.targetBeanName existingInstanceMapping=ext.existingInstanceMapping diff --git a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/PresenceCheckTest.java b/processor/src/test/java/org/mapstruct/ap/test/presencecheck/PresenceCheckTest.java deleted file mode 100644 index 62204c863..000000000 --- a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/PresenceCheckTest.java +++ /dev/null @@ -1,171 +0,0 @@ -/** - * Copyright 2012-2016 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.presencecheck; - -import java.util.ArrayList; -import java.util.List; - -import org.fest.assertions.Assertions; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mapstruct.ap.testutil.WithClasses; -import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; - -/** - * Test for correct handling of source presence checks. - * - * @author Sean Huang - */ -@WithClasses({ - SourceTargetMapper.class, - MyObject.class, - CustomMapper.class, - MyLongWrapper.class, - Source.class, - Target.class, - SourceWtCheck.class, - TargetWtCheck.class -}) -@RunWith(AnnotationProcessorTestRunner.class) -public class PresenceCheckTest { - - @Test - public void testSourceNoPresenceCheckWithIsNullheck() { - SourceWtCheck source = new SourceWtCheck(); - source.setHasSomeList( false ); - source.setHasSomeLong2( false ); - - TargetWtCheck target = SourceTargetMapper.INSTANCE.sourceToTargetWithIsNullCheck( source ); - - //No null check for primitive type - Assert.assertEquals( 0, target.getSomePrimitiveDouble(), 0.01 ); - Assert.assertEquals( null, target.getSomeInteger() ); - Assert.assertEquals( null, target.getNoCheckObject() ); - Assert.assertEquals( 0, target.getNoCheckPrimitive() ); - Assert.assertEquals( null, target.getSomeLong1() ); - Assert.assertEquals( null, target.getSomeLong2() ); - } - - @Test( expected = NullPointerException.class ) - public void testSourceNoPresenceCheckWithIsNullInlineCheck() { - SourceWtCheck source = new SourceWtCheck(); - source.setHasSomeList( false ); - - //No null check for Mapper, if sourceValuePresenceCheckStrategy = IS_NULL_INLINE - TargetWtCheck target = SourceTargetMapper.INSTANCE.sourceToTargetWithIsNullInlineCheck( source ); - - Assert.assertEquals( null, target.getSomeLong2() ); - } - - @Test - public void testSourcePresenceCheckWithCustom() { - MyObject object = new MyObject(); - MyLongWrapper longWrapper = new MyLongWrapper(); - longWrapper.setMyLong( 2L ); - List list = new ArrayList(); - list.add( "first" ); - list.add( "second" ); - - Source source = new Source(); - - source.setSomeObject( object ); - source.setSomePrimitiveDouble( 5.0 ); - source.setSomeInteger( 7 ); - source.setSomeLong1( 2L ); - source.setHasSomeLong2( false ); - source.setSomeList( list ); - - Target target = SourceTargetMapper.INSTANCE.sourceToTargetWithCustom( source ); - - Assert.assertEquals( object, target.getSomeObject() ); - Assert.assertEquals( 5.0, target.getSomePrimitiveDouble(), 0.01 ); - Assert.assertEquals( (Integer) 7, target.getSomeInteger() ); - Assert.assertEquals( longWrapper.getMyLong(), target.getSomeLong1().getMyLong() ); - Assert.assertEquals( null, target.getSomeLong2() ); - - Assertions.assertThat( target.getSomeList() ).containsExactly( "first", "second" ); - } - - @Test - public void testUpdateWithCustom() { - MyObject object = new MyObject(); - MyLongWrapper longWrapper = new MyLongWrapper(); - longWrapper.setMyLong( 2L ); - List list = new ArrayList(); - list.add( "first" ); - list.add( "second" ); - - Source source = new Source(); - - source.setSomeObject( object ); - - source.setSomePrimitiveDouble( 5.0 ); - source.setHasSomePrimitiveDouble( false ); - - source.setSomeInteger( 7 ); - source.setSomeLong1( 2L ); - - source.setSomeLong2( 4L ); - source.setHasSomeLong2( false ); - - source.setSomeList( list ); - - Target target = new Target(); - - SourceTargetMapper.INSTANCE.sourceToTargetWithCustom( source, target ); - - Assert.assertEquals( object, target.getSomeObject() ); - Assert.assertEquals( 0, target.getSomePrimitiveDouble(), 0.01 ); - Assert.assertEquals( (Integer) 7, target.getSomeInteger() ); - Assert.assertEquals( longWrapper.getMyLong(), target.getSomeLong1().getMyLong() ); - Assert.assertEquals( null, target.getSomeLong2() ); - - Assertions.assertThat( target.getSomeList() ).containsExactly( "first", "second" ); - } - - @Test - public void testSourcePresenceCheckWithCustomAndDefaultValue() { - List list = new ArrayList(); - list.add( "first" ); - list.add( "second" ); - - Source source = new Source(); - source.setSomeList( list ); - - source.setHasSomePrimitiveDouble( false ); - source.setHasSomeInteger( false ); - source.setHasSomeLong1( false ); - source.setHasSomeLong2( false ); - - Target target = SourceTargetMapper.INSTANCE.sourceToTargetWithCustomAndDefault( source ); - - Assert.assertEquals( null, target.getSomeObject() ); - - //Support default value for primitive type if there is hasX method and config is on - Assert.assertEquals( 111.1, target.getSomePrimitiveDouble(), 0.01 ); - Assert.assertEquals( (Integer) 222, target.getSomeInteger() ); - Assert.assertEquals( (Long) 333L, target.getSomeLong1().getMyLong() ); - Assert.assertEquals( (Long) 444L, target.getSomeLong2().getMyLong() ); - - for (int i = 0; i < list.size(); i++) { - Assert.assertEquals( list.get( i ), target.getSomeList().get( i ) ); - } - } -} diff --git a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/presencecheck/SourceTargetMapper.java deleted file mode 100644 index 2febd1f0e..000000000 --- a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/SourceTargetMapper.java +++ /dev/null @@ -1,82 +0,0 @@ -/** - * Copyright 2012-2016 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.presencecheck; - -import static org.mapstruct.SourceValuePresenceCheckStrategy.CUSTOM; -import static org.mapstruct.SourceValuePresenceCheckStrategy.IS_NULL; -import static org.mapstruct.SourceValuePresenceCheckStrategy.IS_NULL_INLINE; - -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.MappingTarget; -import org.mapstruct.Mappings; -import org.mapstruct.factory.Mappers; - -/** - * @author Sean Huang - */ -@Mapper(uses = { CustomMapper.class }, sourceValuePresenceCheckStrategy = CUSTOM) -public interface SourceTargetMapper { - - SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); - - Target sourceToTargetWithCustom(Source source); - - void sourceToTargetWithCustom(Source source, @MappingTarget Target target); - - @Mappings( { - @Mapping(target = "somePrimitiveDouble", defaultValue = "111.1"), - @Mapping(target = "someInteger", defaultValue = "222"), - @Mapping(target = "someLong1", defaultValue = "333"), - @Mapping(target = "someLong2", defaultValue = "444"), - } ) - Target sourceToTargetWithCustomAndDefault(Source source); - - @Mappings( { - @Mapping(target = "somePrimitiveDouble", sourceValuePresenceCheckStrategy = IS_NULL), - @Mapping(target = "someInteger", sourceValuePresenceCheckStrategy = IS_NULL), - @Mapping(target = "noCheckObject", sourceValuePresenceCheckStrategy = IS_NULL), - @Mapping(target = "noCheckPrimitive", sourceValuePresenceCheckStrategy = IS_NULL), - @Mapping(target = "someLong1", sourceValuePresenceCheckStrategy = IS_NULL), - } ) - TargetWtCheck sourceToTargetWithIsNullCheck(SourceWtCheck source); - - @Mappings( { - @Mapping(target = "noCheckObject", sourceValuePresenceCheckStrategy = IS_NULL), - @Mapping(target = "noCheckPrimitive", sourceValuePresenceCheckStrategy = IS_NULL), - @Mapping(target = "someLong2", sourceValuePresenceCheckStrategy = IS_NULL_INLINE), - } ) - TargetWtCheck sourceToTargetWithIsNullInlineCheck(SourceWtCheck source); - - /* - * Seeing exception below since there is no presence check method on source. - * - * org.junit.ComparisonFailure: [Compilation failed. Diagnostics: [DiagnosticDescriptor: - * ERROR SourceTargetPresenceCheckMapper.java:53 Using custom source value presence checking strategy, - * but no presence checker found for property "int noCheckPrimitive" in source type.]] - * expected:<[SUCCEED]ED> but was:<[FAIL]ED> - * - @Mappings( { - @Mapping(target = "someDouble", defaultValue = "111.1"), - @Mapping(target = "someInteger", defaultValue = "222"), - @Mapping(target = "someLong", defaultValue = "333"), - } ) - TargetWtCheck sourceToTargetWithConfigOn(SourceWtCheck source); - */ -} diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/PresenceCheckTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/PresenceCheckTest.java new file mode 100644 index 000000000..d038491cf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/PresenceCheckTest.java @@ -0,0 +1,76 @@ +/** + * Copyright 2012-2016 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.nullvaluecheckstrategy; + + +import static org.fest.assertions.Assertions.assertThat; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * + * @author Sjaak Derksen + */ +@WithClasses({ + RockFestivalMapper.class, + RockFestivalSource.class, + RockFestivalTarget.class, + RockFestivalMapperConfig.class, + RockFestivalMapperWithConfig.class, + RockFestivalMapperOveridingConfig.class, + Stage.class +}) +@RunWith(AnnotationProcessorTestRunner.class) +public class PresenceCheckTest { + + @Test + public void testCallingMappingMethodWithNullSource() { + + RockFestivalSource source = new RockFestivalSource(); + RockFestivalTarget target = RockFestivalMapper.INSTANCE.map( source ); + assertThat( target.getStage() ).isNull(); + + source.setArtistName( "New Order" ); + target = RockFestivalMapper.INSTANCE.map( source ); + assertThat( target.getStage() ).isEqualTo( Stage.THE_BARN ); + + } + + @Test + public void testCallingMappingMethodWithNullSourceWithConfig() { + + RockFestivalSource source = new RockFestivalSource(); + RockFestivalTarget target = RockFestivalMapperWithConfig.INSTANCE.map( source ); + assertThat( target.getStage() ).isNull(); + + source.setArtistName( "New Order" ); + target = RockFestivalMapperWithConfig.INSTANCE.map( source ); + assertThat( target.getStage() ).isEqualTo( Stage.THE_BARN ); + + } + + @Test( expected = IllegalArgumentException.class ) + public void testCallingMappingMethodWithNullSourceOveridingConfig() { + + RockFestivalSource source = new RockFestivalSource(); + RockFestivalMapperOveridingConfig.INSTANCE.map( source ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapper.java new file mode 100644 index 000000000..cee061e00 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapper.java @@ -0,0 +1,43 @@ +/** + * Copyright 2012-2016 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.nullvaluecheckstrategy; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.NullValueCheckStrategy; +import org.mapstruct.factory.Mappers; + +/** + * + * @author Sjaak Derksen + */ +@Mapper( nullValueCheckStrategy = NullValueCheckStrategy.ALLWAYS ) +public abstract class RockFestivalMapper { + + public static final RockFestivalMapper INSTANCE = Mappers.getMapper( RockFestivalMapper.class ); + + @Mapping( target = "stage", source = "artistName" ) + public abstract RockFestivalTarget map( RockFestivalSource in ); + + public Stage artistToStage( String name ) { + return Stage.forArtist( name ); + } + + +} diff --git a/core-common/src/main/java/org/mapstruct/SourceValuePresenceCheckStrategy.java b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperConfig.java similarity index 61% rename from core-common/src/main/java/org/mapstruct/SourceValuePresenceCheckStrategy.java rename to processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperConfig.java index 6bf0092f3..9cb829432 100644 --- a/core-common/src/main/java/org/mapstruct/SourceValuePresenceCheckStrategy.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperConfig.java @@ -16,30 +16,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mapstruct; +package org.mapstruct.ap.test.source.nullvaluecheckstrategy; +import org.mapstruct.MapperConfig; +import org.mapstruct.NullValueCheckStrategy; /** - * Strategy to decide how to check null or hasX method before mapping * - * @author Sean Huang + * @author Sjaak Derksen */ -public enum SourceValuePresenceCheckStrategy { +@MapperConfig( nullValueCheckStrategy = NullValueCheckStrategy.ALLWAYS ) +public interface RockFestivalMapperConfig { - /** - * Only check != null for inline conversions - * - */ - IS_NULL_INLINE, - - /** - * Always check != null, no matter whether it's an inline or method conversion - * - */ - IS_NULL, - - /** - * Will invoke custom hasX() method, before mapping, - * name to be given through the accessor naming strategy - */ - CUSTOM; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperOveridingConfig.java b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperOveridingConfig.java new file mode 100644 index 000000000..18d3f5811 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperOveridingConfig.java @@ -0,0 +1,44 @@ +/** + * Copyright 2012-2016 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.nullvaluecheckstrategy; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; +import static org.mapstruct.NullValueCheckStrategy.ON_IMPLICIT_CONVERSION; + +/** + * + * @author Sjaak Derksen + */ +@Mapper( config = RockFestivalMapperConfig.class, nullValueCheckStrategy = ON_IMPLICIT_CONVERSION ) +public abstract class RockFestivalMapperOveridingConfig { + + public static final RockFestivalMapperOveridingConfig INSTANCE = + Mappers.getMapper( RockFestivalMapperOveridingConfig.class ); + + @Mapping( target = "stage", source = "artistName" ) + public abstract RockFestivalTarget map( RockFestivalSource in ); + + public Stage artistToStage( String name ) { + return Stage.forArtist( name ); + } + + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/TargetWtCheck.java b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperWithConfig.java similarity index 56% rename from processor/src/test/java/org/mapstruct/ap/test/presencecheck/TargetWtCheck.java rename to processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperWithConfig.java index c644e221d..e6921c3ad 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/TargetWtCheck.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalMapperWithConfig.java @@ -16,30 +16,28 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mapstruct.ap.test.presencecheck; +package org.mapstruct.ap.test.source.nullvaluecheckstrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; /** - * @author Sean Huang + * + * @author Sjaak Derksen */ -public class TargetWtCheck extends Target { +@Mapper( config = RockFestivalMapperConfig.class ) +public abstract class RockFestivalMapperWithConfig { - private int noCheckPrimitive; - private String noCheckObject; + public static final RockFestivalMapperWithConfig INSTANCE = + Mappers.getMapper( RockFestivalMapperWithConfig.class ); - public int getNoCheckPrimitive() { - return noCheckPrimitive; + @Mapping( target = "stage", source = "artistName" ) + public abstract RockFestivalTarget map( RockFestivalSource in ); + + public Stage artistToStage( String name ) { + return Stage.forArtist( name ); } - public void setNoCheckPrimitive(int noCheckPrimitive) { - this.noCheckPrimitive = noCheckPrimitive; - } - public String getNoCheckObject() { - return noCheckObject; - } - - public void setNoCheckObject(String noCheckObject) { - this.noCheckObject = noCheckObject; - } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/CustomMapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalSource.java similarity index 64% rename from processor/src/test/java/org/mapstruct/ap/test/presencecheck/CustomMapper.java rename to processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalSource.java index 33b5fd04c..0cc04c9ac 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/CustomMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalSource.java @@ -16,20 +16,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mapstruct.ap.test.presencecheck; - -import org.mapstruct.Mapper; -import org.mapstruct.SourceValuePresenceCheckStrategy; +package org.mapstruct.ap.test.source.nullvaluecheckstrategy; /** - * @author Sean Huang + * + * @author Sjaak Derksen */ -@Mapper( sourceValuePresenceCheckStrategy = SourceValuePresenceCheckStrategy.IS_NULL_INLINE ) -public class CustomMapper { +public class RockFestivalSource { - public MyLongWrapper toMyLongWrapperViaPrimitive(Long primitive) { - MyLongWrapper wrapper = new MyLongWrapper(); - wrapper.setMyLong( primitive ); - return wrapper; + private String artistName; + + public String getArtistName() { + return artistName; } + + public void setArtistName(String artistName) { + this.artistName = artistName; + } + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/MyLongWrapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalTarget.java similarity index 77% rename from processor/src/test/java/org/mapstruct/ap/test/presencecheck/MyLongWrapper.java rename to processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalTarget.java index 8e28a9e7d..4638036f1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/MyLongWrapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/RockFestivalTarget.java @@ -16,23 +16,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mapstruct.ap.test.presencecheck; - +package org.mapstruct.ap.test.source.nullvaluecheckstrategy; /** + * * @author Sjaak Derksen */ -public class MyLongWrapper { +public class RockFestivalTarget { - private Long myLong; + private Stage stage; - public Long getMyLong() { - return myLong; + public Stage getStage() { + return stage; } - public void setMyLong(Long myLong) { - myLong.longValue(); - this.myLong = myLong; + public void setStage(Stage stage) { + this.stage = stage; } } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/Stage.java b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/Stage.java new file mode 100644 index 000000000..686a89daf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/nullvaluecheckstrategy/Stage.java @@ -0,0 +1,53 @@ +/** + * Copyright 2012-2016 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.nullvaluecheckstrategy; + +import java.util.Arrays; +import java.util.List; + +/** + * + * @author Sjaak Derksen + */ +public enum Stage { + + MAIN("Paul McCartney", "Ellie Goulding", "Disclosure", "Kaiser Chiefs", "Rammstein"), + KLUB_C("James Blake", "Lost Frequencies"), + THE_BARN("New Order", "Year and Years"); + + private final List artists; + + Stage(String... artist) { + this.artists = Arrays.asList( artist ); + } + + public static Stage forArtist( String name ) { + + if ( name == null ) { + throw new IllegalArgumentException(); + } + + for ( Stage value : Stage.values() ) { + if ( value.artists.contains( name ) ) { + return value; + } + } + return null; + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/SourceWtCheck.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/GoalKeeper.java similarity index 61% rename from processor/src/test/java/org/mapstruct/ap/test/presencecheck/SourceWtCheck.java rename to processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/GoalKeeper.java index f21de82ff..1b08ecdb4 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/SourceWtCheck.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/GoalKeeper.java @@ -16,30 +16,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mapstruct.ap.test.presencecheck; - +package org.mapstruct.ap.test.source.presencecheck.spi; /** - * @author Sean Huang + * + * @author Sjaak Derksen */ -public class SourceWtCheck extends Source { +public class GoalKeeper { - private int noCheckPrimitive; - private String noCheckObject; + private String name; + private boolean hasName = true; - public int getNoCheckPrimitive() { - return noCheckPrimitive; + public String getName() { + return name; } - public void setNoCheckPrimitive(int noCheckPrimitive) { - this.noCheckPrimitive = noCheckPrimitive; + public void setName(String name) { + this.name = name; } - public String getNoCheckObject() { - return noCheckObject; + public boolean hasName() { + return hasName; } - public void setNoCheckObject(String noCheckObject) { - this.noCheckObject = noCheckObject; + public void setHasName(boolean hasName) { + this.hasName = hasName; } + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckTest.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckTest.java new file mode 100644 index 000000000..2b4a25e1d --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/PresenceCheckTest.java @@ -0,0 +1,217 @@ +/** + * Copyright 2012-2016 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.presencecheck.spi; + +import java.util.Arrays; +import static org.fest.assertions.Assertions.assertThat; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.spi.PresenceCheckAccessorNamingStrategy; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; +import org.mapstruct.ap.testutil.WithServiceImplementation; + +/** + * Test for correct handling of source presence checks. + * + * @author Sean Huang + */ +@WithClasses({ + SourceTargetMapper.class, + Source.class, + Target.class, + SoccerTeamMapper.class, + SoccerTeamSource.class, + GoalKeeper.class, + SoccerTeamTarget.class +}) +@WithServiceImplementation( PresenceCheckAccessorNamingStrategy.class ) +@RunWith(AnnotationProcessorTestRunner.class) +public class PresenceCheckTest { + + @Test + public void testWithSourcesPresent() { + + Source source = new Source(); + + source.setSomePrimitiveDouble( 5.0 ); + source.setSomeInteger( 7 ); + source.setSomeList( Arrays.asList( "first", "second" ) ); + source.setSomeArray( new String[]{ "x", "y" } ); + + Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target.getSomePrimitiveDouble() ).isEqualTo( 5.0 ); + assertThat( target.getSomeInteger() ).isEqualTo( 7 ); + assertThat( target.getSomeList() ).containsExactly( "first", "second" ); + assertThat( target.getSomeArray() ).isEqualTo( new String[]{ "x", "y"} ); + } + + @Test + public void testWithSourcesAbsent() { + + Source source = new Source(); + + source.setHasSomePrimitiveDouble( false ); + source.setHasSomeInteger( false ); + source.setHasSomeList( false ); + source.setHasSomeArray( false ); + + Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source ); + + assertThat( target.getSomePrimitiveDouble() ).isEqualTo( 0d ); + assertThat( target.getSomeInteger() ).isNull(); + assertThat( target.getSomeList() ).isNull(); + assertThat( target.getSomeArray() ).isNull(); + } + + @Test + public void testUpdateMethodWithSourcesPresent() { + + Source source = new Source(); + + source.setSomePrimitiveDouble( 5.0 ); + source.setSomeInteger( 7 ); + source.setSomeList( Arrays.asList( "first", "second" ) ); + source.setSomeArray( new String[]{ "x", "y" } ); + + Target target = new Target(); + SourceTargetMapper.INSTANCE.sourceToTarget( source, target ); + + assertThat( target.getSomePrimitiveDouble() ).isEqualTo( 5.0 ); + assertThat( target.getSomeInteger() ).isEqualTo( 7 ); + assertThat( target.getSomeList() ).containsExactly( "first", "second" ); + assertThat( target.getSomeArray() ).isEqualTo( new String[]{ "x", "y"} ); + } + + @Test + public void testUpdateMethodWithSourcesAbsent() { + + Source source = new Source(); + + source.setHasSomePrimitiveDouble( false ); + source.setHasSomeInteger( false ); + source.setHasSomeList( false ); + source.setHasSomeArray( false ); + + Target target = new Target(); + SourceTargetMapper.INSTANCE.sourceToTarget( source, target ); + + assertThat( target.getSomePrimitiveDouble() ).isEqualTo( 0d ); + assertThat( target.getSomeInteger() ).isNull(); + assertThat( target.getSomeList() ).isNull(); + assertThat( target.getSomeArray() ).isNull(); + } + + @Test + public void testWithSourcesPresentAndDefault() { + + Source source = new Source(); + + source.setSomePrimitiveDouble( 5.0 ); + source.setSomeInteger( 7 ); + source.setSomeList( Arrays.asList( "first", "second" ) ); + source.setSomeArray( new String[]{ "x", "y" } ); + + Target target = SourceTargetMapper.INSTANCE.sourceToTargetWitDefaults( source ); + + assertThat( target.getSomePrimitiveDouble() ).isEqualTo( 5.0 ); + assertThat( target.getSomeInteger() ).isEqualTo( 7 ); + assertThat( target.getSomeList() ).containsExactly( "first", "second" ); + assertThat( target.getSomeArray() ).isEqualTo( new String[]{ "x", "y"} ); + } + + @Test + public void testWithSourcesAbsentAndDefault() { + + Source source = new Source(); + + source.setHasSomePrimitiveDouble( false ); + source.setHasSomeInteger( false ); + source.setHasSomeList( false ); + source.setHasSomeArray( false ); + + Target target = SourceTargetMapper.INSTANCE.sourceToTargetWitDefaults( source ); + + assertThat( target.getSomePrimitiveDouble() ).isEqualTo( 111.1d ); + assertThat( target.getSomeInteger() ).isEqualTo( 222 ); + assertThat( target.getSomeList() ).containsExactly( "a", "b" ); + assertThat( target.getSomeArray() ).isEqualTo( new String[]{ "u", "v"} ); + } + + @Test + public void testAdderWithSourcesPresent() { + + SoccerTeamSource soccerTeamSource = new SoccerTeamSource(); + soccerTeamSource.setPlayers( Arrays.asList( "pele", "cruyf" ) ); + + SoccerTeamTarget target = SoccerTeamMapper.INSTANCE.mapAdder( soccerTeamSource ); + + assertThat( target.getPlayers() ).containsExactly( "pele", "cruyf" ); + } + + @Test + public void testAdderWithSourcesAbsent() { + + SoccerTeamSource soccerTeamSource = new SoccerTeamSource(); + soccerTeamSource.setHasPlayers( false ); + + SoccerTeamTarget target = SoccerTeamMapper.INSTANCE.mapAdder( soccerTeamSource ); + + assertThat( target.getPlayers() ).isNull(); + } + + @Test + public void testNestedWithSourcesPresent() { + + SoccerTeamSource soccerTeamSource = new SoccerTeamSource(); + GoalKeeper goalKeeper = new GoalKeeper(); + goalKeeper.setName( "Buffon" ); + soccerTeamSource.setGoalKeeper( goalKeeper ); + + SoccerTeamTarget target = SoccerTeamMapper.INSTANCE.mapNested( soccerTeamSource ); + + assertThat( target.getGoalKeeperName() ).isEqualTo( "Buffon" ); + } + + @Test + public void testNestedWithSourcesAbsentOnRootLevel() { + + SoccerTeamSource soccerTeamSource = new SoccerTeamSource(); + soccerTeamSource.setHasGoalKeeper( false ); + + SoccerTeamTarget target = SoccerTeamMapper.INSTANCE.mapNested( soccerTeamSource ); + + assertThat( target.getGoalKeeperName() ).isNull(); + } + + @Test + public void testNestedWithSourcesAbsentOnNestingLevel() { + + SoccerTeamSource soccerTeamSource = new SoccerTeamSource(); + GoalKeeper goalKeeper = new GoalKeeper(); + goalKeeper.setHasName( false ); + soccerTeamSource.setGoalKeeper( goalKeeper ); + + SoccerTeamTarget target = SoccerTeamMapper.INSTANCE.mapNested( soccerTeamSource ); + + assertThat( target.getGoalKeeperName() ).isNull(); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/internal/prism/SourceValuePresenceCheckStrategy.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamMapper.java similarity index 51% rename from processor/src/main/java/org/mapstruct/ap/internal/prism/SourceValuePresenceCheckStrategy.java rename to processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamMapper.java index 5f2425d89..e1b906c2c 100644 --- a/processor/src/main/java/org/mapstruct/ap/internal/prism/SourceValuePresenceCheckStrategy.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamMapper.java @@ -16,30 +16,31 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mapstruct.ap.internal.prism; +package org.mapstruct.ap.test.source.presencecheck.spi; +import org.mapstruct.CollectionMappingStrategy; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; /** - * Prism for the enum {@link org.mapstruct.SourceValuePresenceCheckStrategy} * - * @author Sean Huang + * @author Sjaak Derksen */ -public enum SourceValuePresenceCheckStrategy { - /** - * Only check != null for inline conversions - * - */ - IS_NULL_INLINE, +@Mapper( collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED ) +public interface SoccerTeamMapper { - /** - * Always check != null, no matter whether it's an inline or method conversion - * - */ - IS_NULL, + SoccerTeamMapper INSTANCE = Mappers.getMapper( SoccerTeamMapper.class ); + + @Mapping( target = "goalKeeperName", ignore = true ) + SoccerTeamTarget mapAdder( SoccerTeamSource in ); + + + @Mappings({ + @Mapping(target = "players", ignore = true), + @Mapping(target = "goalKeeperName", source = "goalKeeper.name") + }) + SoccerTeamTarget mapNested( SoccerTeamSource in ); - /** - * Will invoke custom hasX() method, before mapping, - * name to be given through the accessor naming strategy - */ - CUSTOM; } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamSource.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamSource.java new file mode 100644 index 000000000..a7e794293 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamSource.java @@ -0,0 +1,66 @@ +/** + * Copyright 2012-2016 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.presencecheck.spi; + +import java.util.List; + +/** + * @author Sjaak Derksen + */ +public class SoccerTeamSource { + + private List players; + private boolean hasPlayers = true; + + private GoalKeeper goalKeeper; + private boolean hasGoalKeeper = true; + + public boolean hasPlayers() { + return hasPlayers; + } + + public void setHasPlayers(boolean has) { + this.hasPlayers = has; + } + + public List getPlayers() { + return players; + } + + public void setPlayers(List players) { + this.players = players; + } + + public GoalKeeper getGoalKeeper() { + return goalKeeper; + } + + public void setGoalKeeper(GoalKeeper goalKeeper) { + this.goalKeeper = goalKeeper; + } + + public boolean hasGoalKeeper() { + return hasGoalKeeper; + } + + public void setHasGoalKeeper(boolean hasGoalKeeper) { + this.hasGoalKeeper = hasGoalKeeper; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamTarget.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamTarget.java new file mode 100644 index 000000000..2ecb92d4e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SoccerTeamTarget.java @@ -0,0 +1,52 @@ +/** + * Copyright 2012-2016 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.presencecheck.spi; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Sjaak Derksen + */ +public class SoccerTeamTarget { + + private List players; + private String goalKeeperName; + + + public List getPlayers() { + return players; + } + + public void addPlayer(String player) { + if ( this.players == null ) { + this.players = new ArrayList(); + } + this.players.add( player ); + } + + public String getGoalKeeperName() { + return goalKeeperName; + } + + public void setGoalKeeperName(String goalKeeperName) { + this.goalKeeperName = goalKeeperName; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/Source.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/Source.java similarity index 64% rename from processor/src/test/java/org/mapstruct/ap/test/presencecheck/Source.java rename to processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/Source.java index 00e52ef03..34f325afb 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/Source.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/Source.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mapstruct.ap.test.presencecheck; +package org.mapstruct.ap.test.source.presencecheck.spi; import java.util.List; @@ -25,39 +25,17 @@ import java.util.List; */ public class Source { - private MyObject someObject; - private boolean hasSomeObject = true; - private double somePrimitiveDouble; private boolean hasPrimitiveSomeDouble = true; private Integer someInteger; private boolean hasSomeInteger = true; - private Long someLong1; - private boolean hasSomeLong1 = true; - - private Long someLong2; - private boolean hasSomeLong2 = true; - private List someList; private boolean hasSomeList = true; - public boolean hasSomeObject() { - return hasSomeObject; - } - - public void setHasSomeObject(boolean has) { - this.hasSomeObject = has; - } - - public MyObject getSomeObject() { - return someObject; - } - - public void setSomeObject(MyObject someObject) { - this.someObject = someObject; - } + private String[] someArray; + private boolean hasSomeArray = true; public boolean hasSomePrimitiveDouble() { return hasPrimitiveSomeDouble; @@ -91,38 +69,6 @@ public class Source { this.someInteger = someInteger; } - public boolean hasSomeLong1() { - return hasSomeLong1; - } - - public void setHasSomeLong1(boolean hasSomeInLong) { - this.hasSomeLong1 = hasSomeInLong; - } - - public boolean hasSomeLong2() { - return hasSomeLong2; - } - - public void setHasSomeLong2(boolean hasSomeInLong) { - this.hasSomeLong2 = hasSomeInLong; - } - - public Long getSomeLong1() { - return someLong1; - } - - public Long getSomeLong2() { - return someLong2; - } - - public void setSomeLong1(Long someLong) { - this.someLong1 = someLong; - } - - public void setSomeLong2(Long someLong) { - this.someLong2 = someLong; - } - public boolean hasSomeList() { return hasSomeList; } @@ -138,4 +84,21 @@ public class Source { public void setSomeList(List someList) { this.someList = someList; } + + public String[] getSomeArray() { + return someArray; + } + + public void setSomeArray(String[] someArray) { + this.someArray = someArray; + } + + public boolean hasSomeArray() { + return hasSomeArray; + } + + public void setHasSomeArray(boolean hasSomeArray) { + this.hasSomeArray = hasSomeArray; + } + } diff --git a/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SourceTargetMapper.java new file mode 100644 index 000000000..c6d933079 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/SourceTargetMapper.java @@ -0,0 +1,59 @@ +/** + * Copyright 2012-2016 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.presencecheck.spi; + +import java.util.Arrays; +import java.util.List; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.MappingTarget; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; + + +/** + * @author Sean Huang + */ +@Mapper +public abstract class SourceTargetMapper { + + public static final SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); + + //@Mapping( target = "someInteger2", source = "someNested.someInteger2" ) + abstract Target sourceToTarget(Source source); + + abstract void sourceToTarget(Source source, @MappingTarget Target target); + + @Mappings( { + @Mapping(target = "somePrimitiveDouble", defaultValue = "111.1"), + @Mapping(target = "someInteger", defaultValue = "222"), + @Mapping(target = "someList", defaultValue = "a,b"), + @Mapping(target = "someArray", defaultValue = "u,v") + } ) + abstract Target sourceToTargetWitDefaults(Source source); + + + protected List toList( String in ) { + return Arrays.asList( in.split( "," ) ); + } + + protected String[] toArray( String in ) { + return in.split( "," ); + } +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/Target.java b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/Target.java similarity index 70% rename from processor/src/test/java/org/mapstruct/ap/test/presencecheck/Target.java rename to processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/Target.java index 9d54e6d42..2a2ccaab5 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/presencecheck/Target.java +++ b/processor/src/test/java/org/mapstruct/ap/test/source/presencecheck/spi/Target.java @@ -16,7 +16,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.mapstruct.ap.test.presencecheck; +package org.mapstruct.ap.test.source.presencecheck.spi; import java.util.List; @@ -25,20 +25,10 @@ import java.util.List; */ public class Target { - private MyObject someObject; private double somePrimitiveDouble; private Integer someInteger; - private MyLongWrapper someLong1; - private MyLongWrapper someLong2; private List someList; - - public MyObject getSomeObject() { - return someObject; - } - - public void setSomeObject(MyObject someObject) { - this.someObject = someObject; - } + private String[] someArray; public double getSomePrimitiveDouble() { return somePrimitiveDouble; @@ -56,22 +46,6 @@ public class Target { this.someInteger = someInteger; } - public MyLongWrapper getSomeLong1() { - return someLong1; - } - - public void setSomeLong1(MyLongWrapper someLong) { - this.someLong1 = someLong; - } - - public MyLongWrapper getSomeLong2() { - return someLong2; - } - - public void setSomeLong2(MyLongWrapper someLong) { - this.someLong2 = someLong; - } - public List getSomeList() { return someList; } @@ -79,4 +53,13 @@ public class Target { public void setSomeList(List someList) { this.someList = someList; } + + public String[] getSomeArray() { + return someArray; + } + + public void setSomeArray(String[] someArray) { + this.someArray = someArray; + } + }