#669 rework into sourcepresencecheck SPI and nullvaluecheckstrategy

This commit is contained in:
sjaakd 2016-06-21 23:55:39 +02:00
parent 1af4441d2d
commit b1f03689d9
40 changed files with 1079 additions and 694 deletions

View File

@ -17,6 +17,7 @@ Remko Plantenga
Samuel Wright Samuel Wright
Sebastian Hasait Sebastian Hasait
Sjaak Derksen Sjaak Derksen
Sean Huang
Timo Eckhardt Timo Eckhardt
Tomek Gubala Tomek Gubala
Vincent Alexander Beelte Vincent Alexander Beelte

View File

@ -18,14 +18,13 @@
*/ */
package org.mapstruct; package org.mapstruct;
import static org.mapstruct.SourceValuePresenceCheckStrategy.IS_NULL_INLINE;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import org.mapstruct.factory.Mappers; 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 * 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; 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}. * 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;
} }

View File

@ -18,14 +18,13 @@
*/ */
package org.mapstruct; package org.mapstruct;
import static org.mapstruct.SourceValuePresenceCheckStrategy.IS_NULL_INLINE;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import org.mapstruct.factory.Mappers; 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 * 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; 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}. * 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;
} }

View File

@ -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.
*
* <b>Note:</b> 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:
* <p>
* <ol>
* <li>a source value is directly assigned to a target</li>
* <li>a source value assigned to a target by calling a type conversion on the target first</li>
* </ol>
* <p>
* <b>NOTE:</b> 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:
* <p>
* <ol>
* <li>a source value is directly assigned to a target</li>
* <li>a source value assigned to a target by calling a type conversion on the target first</li>
* </ol>
* <p>
* <b>NOTE:</b> 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,
}

View File

@ -18,8 +18,6 @@
*/ */
package org.mapstruct; package org.mapstruct;
import static org.mapstruct.SourceValuePresenceCheckStrategy.IS_NULL;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable; 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}. * @return Default value to set in case the source property is {@code null}.
*/ */
String defaultValue() default ""; 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;
} }

View File

@ -18,8 +18,6 @@
*/ */
package org.mapstruct; package org.mapstruct;
import static org.mapstruct.SourceValuePresenceCheckStrategy.IS_NULL_INLINE;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; 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}. * @return Default value to set in case the source property is {@code null}.
*/ */
String defaultValue() default ""; 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;
} }

View File

@ -370,7 +370,7 @@ public class BeanMappingMethod extends MappingMethod {
while ( targetPropertiesIterator.hasNext() ) { while ( targetPropertiesIterator.hasNext() ) {
Entry<String, ExecutableElement> targetProperty = targetPropertiesIterator.next(); Entry<String, ExecutableElement> targetProperty = targetPropertiesIterator.next();
String propertyName = targetProperty.getKey(); String targetPropertyName = targetProperty.getKey();
PropertyMapping propertyMapping = null; PropertyMapping propertyMapping = null;
@ -387,10 +387,10 @@ public class BeanMappingMethod extends MappingMethod {
PropertyMapping newPropertyMapping = null; PropertyMapping newPropertyMapping = null;
ExecutableElement sourceReadAccessor = ExecutableElement sourceReadAccessor =
sourceParameter.getType().getPropertyReadAccessors().get( propertyName ); sourceParameter.getType().getPropertyReadAccessors().get( targetPropertyName );
ExecutableElement sourcePresenceChecker = ExecutableElement sourcePresenceChecker =
sourceParameter.getType().getPropertyPresenceCheckers().get( propertyName ); sourceParameter.getType().getPropertyPresenceCheckers().get( targetPropertyName );
if ( sourceReadAccessor != null ) { if ( sourceReadAccessor != null ) {
Mapping mapping = method.getSingleMappingByTargetPropertyName( targetProperty.getKey() ); Mapping mapping = method.getSingleMappingByTargetPropertyName( targetProperty.getKey() );
@ -408,8 +408,8 @@ public class BeanMappingMethod extends MappingMethod {
.mappingContext( ctx ) .mappingContext( ctx )
.sourceMethod( method ) .sourceMethod( method )
.targetWriteAccessor( targetProperty.getValue() ) .targetWriteAccessor( targetProperty.getValue() )
.targetReadAccessor( getTargetPropertyReadAccessor( propertyName ) ) .targetReadAccessor( getTargetPropertyReadAccessor( targetPropertyName ) )
.targetPropertyName( propertyName ) .targetPropertyName( targetPropertyName )
.sourceReference( sourceRef ) .sourceReference( sourceRef )
.formattingParameters( mapping != null ? mapping.getFormattingParameters() : null ) .formattingParameters( mapping != null ? mapping.getFormattingParameters() : null )
.selectionParameters( mapping != null ? mapping.getSelectionParameters() : null ) .selectionParameters( mapping != null ? mapping.getSelectionParameters() : null )
@ -426,7 +426,7 @@ public class BeanMappingMethod extends MappingMethod {
ctx.getMessager().printMessage( ctx.getMessager().printMessage(
method.getExecutable(), method.getExecutable(),
Message.BEANMAPPING_SEVERAL_POSSIBLE_SOURCES, Message.BEANMAPPING_SEVERAL_POSSIBLE_SOURCES,
propertyName targetPropertyName
); );
break; break;
} }

View File

@ -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.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;
import static org.mapstruct.ap.internal.model.assignment.Assignment.AssignmentType.TYPE_CONVERTED_MAPPED; 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.Arrays;
import java.util.Collections; 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.SourceMethod;
import org.mapstruct.ap.internal.model.source.SourceReference; import org.mapstruct.ap.internal.model.source.SourceReference;
import org.mapstruct.ap.internal.model.source.SourceReference.PropertyEntry; 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.Executables;
import org.mapstruct.ap.internal.util.MapperConfiguration; import org.mapstruct.ap.internal.util.MapperConfiguration;
import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Message;
@ -235,23 +237,14 @@ public class PropertyMapping extends ModelElement {
if ( assignment != null ) { if ( assignment != null ) {
if ( targetType.isCollectionOrMapType() ) { if ( targetType.isCollectionOrMapType() ) {
assignment = assignCollection( targetType, targetWriteAccessorType, assignment ); assignment = assignToCollection( targetType, targetWriteAccessorType, assignment );
} }
else if ( targetType.isArrayType() && sourceType.isArrayType() && assignment.getType() == DIRECT ) { else if ( targetType.isArrayType() && sourceType.isArrayType() && assignment.getType() == DIRECT ) {
Type arrayType = ctx.getTypeFactory().getType( Arrays.class ); assignment = assignToArray( targetType, assignment );
assignment = new ArrayCopyWrapper(
assignment,
targetPropertyName,
arrayType,
targetType,
existingVariableNames
);
assignment = new NullCheckWrapper( assignment );
} }
else { else {
assignment = assignObject( sourceType, targetType, targetWriteAccessorType, assignment ); assignment = assignToPlain( sourceType, targetType, targetWriteAccessorType, assignment );
} }
} }
else { else {
ctx.getMessager().printMessage( ctx.getMessager().printMessage(
@ -278,7 +271,9 @@ public class PropertyMapping extends ModelElement {
} }
private Assignment getDefaultValueAssignment() { 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() PropertyMapping build = new ConstantMappingBuilder()
.constantExpression( '"' + defaultValue + '"' ) .constantExpression( '"' + defaultValue + '"' )
.formattingParameters( formattingParameters ) .formattingParameters( formattingParameters )
@ -296,43 +291,80 @@ public class PropertyMapping extends ModelElement {
return null; return null;
} }
private Assignment assignObject(Type sourceType, Type targetType, TargetWriteAccessorType targetAccessorType, private Assignment assignToPlain(Type sourceType, Type targetType, TargetWriteAccessorType targetAccessorType,
Assignment rhs) { Assignment rightHandSide) {
Assignment result = rhs; Assignment result;
if ( targetAccessorType == TargetWriteAccessorType.SETTER ) { if ( targetAccessorType == TargetWriteAccessorType.SETTER ) {
if ( result.isUpdateMethod() ) { result = assignToPlainViaSetter( sourceType, targetType, rightHandSide );
}
else {
result = assignToPlainViaAdder( sourceType, rightHandSide );
}
return result;
}
private Assignment assignToPlainViaSetter(Type sourceType, Type targetType, Assignment rightHandSide) {
Assignment result;
if ( rightHandSide.isUpdateMethod() ) {
if ( targetReadAccessor == null ) { if ( targetReadAccessor == null ) {
ctx.getMessager().printMessage( method.getExecutable(), ctx.getMessager().printMessage( method.getExecutable(),
Message.PROPERTYMAPPING_NO_READ_ACCESSOR_FOR_TARGET_TYPE, Message.PROPERTYMAPPING_NO_READ_ACCESSOR_FOR_TARGET_TYPE,
targetPropertyName ); targetPropertyName );
} }
Assignment factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, targetType, null ); Assignment factoryMethod = ctx.getMappingResolver().getFactoryMethod( method, targetType, null );
result = new UpdateWrapper( result, method.getThrownTypes(), factoryMethod, result = new UpdateWrapper( rightHandSide, method.getThrownTypes(), factoryMethod,
targetType ); targetType );
} }
else { else {
result = new SetterWrapper( result, method.getThrownTypes() ); result = new SetterWrapper( rightHandSide, method.getThrownTypes() );
} }
if ( !sourceType.isPrimitive()
&& !sourceReference.getPropertyEntries().isEmpty() ) { // parameter null taken care of by beanmapper // 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() ) { if ( result.isUpdateMethod() ) {
result = new UpdateNullCheckWrapper( result ); 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 else if ( result.getType() == TYPE_CONVERTED
|| result.getType() == TYPE_CONVERTED_MAPPED || result.getType() == TYPE_CONVERTED_MAPPED
|| result.getType() == MAPPED_TYPE_CONVERTED || result.getType() == MAPPED_TYPE_CONVERTED
|| ( result.getType() == DIRECT && targetType.isPrimitive() ) ) { || (result.getType() == DIRECT && targetType.isPrimitive()) ) {
// for primitive types null check is not possible at all, but a conversion needs // for primitive types null check is not possible at all, but a conversion needs
// a null check. // a null check.
result = new NullCheckWrapper( result ); result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() );
} }
} }
return result;
} }
else {
// TargetAccessorType must be ADDER
private Assignment assignToPlainViaAdder(Type sourceType, Assignment rightHandSide) {
Assignment result = rightHandSide;
if ( getSourceType().isCollectionType() ) { if ( getSourceType().isCollectionType() ) {
result = new AdderWrapper( result = new AdderWrapper(
result, result,
@ -340,19 +372,17 @@ public class PropertyMapping extends ModelElement {
getSourceRef(), getSourceRef(),
sourceType sourceType
); );
result = new NullCheckWrapper( result ); result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() );
} }
else { else {
// Possibly adding null to a target collection. So should be surrounded by an null check. // Possibly adding null to a target collection. So should be surrounded by an null check.
result = new SetterWrapper( result, method.getThrownTypes() ); result = new SetterWrapper( result, method.getThrownTypes() );
result = new NullCheckWrapper( result ); result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() );
}
} }
return result; return result;
} }
private Assignment assignCollection(Type targetType, private Assignment assignToCollection(Type targetType,
TargetWriteAccessorType targetAccessorType, TargetWriteAccessorType targetAccessorType,
Assignment rhs) { Assignment rhs) {
@ -419,15 +449,28 @@ public class PropertyMapping extends ModelElement {
// for mapping methods (builtin / custom), the mapping method is responsible for the // for mapping methods (builtin / custom), the mapping method is responsible for the
// null check. Typeconversions do not apply to collections and maps. // null check. Typeconversions do not apply to collections and maps.
if ( result.getType() == DIRECT ) { if ( result.getType() == DIRECT ) {
result = new NullCheckWrapper( result ); result = new NullCheckWrapper( result, getSourcePresenceCheckerRef() );
} }
else if ( result.getType() == MAPPED && result.isUpdateMethod() ) { else if ( result.getType() == MAPPED && result.isUpdateMethod() ) {
result = new UpdateNullCheckWrapper( result ); result = new UpdateNullCheckWrapper( result, getSourcePresenceCheckerRef() );
} }
return result; 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() { private Type getSourceType() {
Parameter sourceParam = sourceReference.getParameter(); 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, private Assignment forgeMapOrIterableMapping(Type sourceType, Type targetType, String sourceReference,
ExecutableElement element) { ExecutableElement element) {

View File

@ -25,7 +25,15 @@ package org.mapstruct.ap.internal.model.assignment;
*/ */
public class NullCheckWrapper extends AssignmentWrapper { public class NullCheckWrapper extends AssignmentWrapper {
public NullCheckWrapper( Assignment decoratedAssignment ) { private final String sourcePresenceChecker;
public NullCheckWrapper( Assignment decoratedAssignment, String sourcePresenceChecker ) {
super( decoratedAssignment ); super( decoratedAssignment );
this.sourcePresenceChecker = sourcePresenceChecker;
}
public String getSourcePresenceChecker() {
return sourcePresenceChecker;
} }
} }

View File

@ -26,7 +26,15 @@ package org.mapstruct.ap.internal.model.assignment;
*/ */
public class UpdateNullCheckWrapper extends AssignmentWrapper { public class UpdateNullCheckWrapper extends AssignmentWrapper {
public UpdateNullCheckWrapper( Assignment decoratedAssignment ) { private final String sourcePresenceChecker;
public UpdateNullCheckWrapper( Assignment decoratedAssignment, String sourcePresenceChecker ) {
super( decoratedAssignment ); super( decoratedAssignment );
this.sourcePresenceChecker = sourcePresenceChecker;
} }
public String getSourcePresenceChecker() {
return sourcePresenceChecker;
}
} }

View File

@ -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.CollectionMappingStrategyPrism;
import org.mapstruct.ap.internal.prism.MappingPrism; import org.mapstruct.ap.internal.prism.MappingPrism;
import org.mapstruct.ap.internal.prism.MappingsPrism; 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.FormattingMessager;
import org.mapstruct.ap.internal.util.Message; import org.mapstruct.ap.internal.util.Message;
@ -62,9 +61,6 @@ public class Mapping {
private final boolean isIgnored; private final boolean isIgnored;
private final List<String> dependsOn; private final List<String> dependsOn;
private final SourceValuePresenceCheckStrategy valuePresenceCheckStrategy;
private final boolean isSetValuePresenceCheckStrategy;
private final AnnotationMirror mirror; private final AnnotationMirror mirror;
private final AnnotationValue sourceAnnotationValue; private final AnnotationValue sourceAnnotationValue;
private final AnnotationValue targetAnnotationValue; private final AnnotationValue targetAnnotationValue;
@ -142,7 +138,6 @@ public class Mapping {
List<String> dependsOn = List<String> dependsOn =
mappingPrism.dependsOn() != null ? mappingPrism.dependsOn() : Collections.<String>emptyList(); mappingPrism.dependsOn() != null ? mappingPrism.dependsOn() : Collections.<String>emptyList();
boolean isSetValuePresenceCheckStrategy = mappingPrism.values.sourceValuePresenceCheckStrategy() != null;
FormattingParameters formattingParam = new FormattingParameters( dateFormat, numberFormat ); FormattingParameters formattingParam = new FormattingParameters( dateFormat, numberFormat );
SelectionParameters selectionParams = new SelectionParameters( SelectionParameters selectionParams = new SelectionParameters(
@ -163,9 +158,7 @@ public class Mapping {
formattingParam, formattingParam,
selectionParams, selectionParams,
mappingPrism.values.dependsOn(), mappingPrism.values.dependsOn(),
dependsOn, dependsOn
SourceValuePresenceCheckStrategy.valueOf( mappingPrism.sourceValuePresenceCheckStrategy() ),
isSetValuePresenceCheckStrategy
); );
} }
@ -174,9 +167,7 @@ public class Mapping {
String defaultValue, boolean isIgnored, AnnotationMirror mirror, String defaultValue, boolean isIgnored, AnnotationMirror mirror,
AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue, AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue,
FormattingParameters formattingParameters, SelectionParameters selectionParameters, FormattingParameters formattingParameters, SelectionParameters selectionParameters,
AnnotationValue dependsOnAnnotationValue, List<String> dependsOn, AnnotationValue dependsOnAnnotationValue, List<String> dependsOn ) {
SourceValuePresenceCheckStrategy valuePresenceCheckStrategy,
boolean isSetValuePresenceCheckStrategy ) {
this.sourceName = sourceName; this.sourceName = sourceName;
this.constant = constant; this.constant = constant;
this.javaExpression = javaExpression; this.javaExpression = javaExpression;
@ -190,8 +181,6 @@ public class Mapping {
this.selectionParameters = selectionParameters; this.selectionParameters = selectionParameters;
this.dependsOnAnnotationValue = dependsOnAnnotationValue; this.dependsOnAnnotationValue = dependsOnAnnotationValue;
this.dependsOn = dependsOn; this.dependsOn = dependsOn;
this.valuePresenceCheckStrategy = valuePresenceCheckStrategy;
this.isSetValuePresenceCheckStrategy = isSetValuePresenceCheckStrategy;
} }
private static String getExpression(MappingPrism mappingPrism, ExecutableElement element, private static String getExpression(MappingPrism mappingPrism, ExecutableElement element,
@ -292,14 +281,6 @@ public class Mapping {
return dependsOn; return dependsOn;
} }
public SourceValuePresenceCheckStrategy sourceValuePresenceCheckStrategy() {
return valuePresenceCheckStrategy;
}
public boolean isSetSourceValuePresenceCheckStrategy() {
return isSetValuePresenceCheckStrategy;
}
private boolean hasPropertyInReverseMethod(String name, SourceMethod method) { private boolean hasPropertyInReverseMethod(String name, SourceMethod method) {
CollectionMappingStrategyPrism cms = method.getMapperConfiguration().getCollectionMappingStrategy(); CollectionMappingStrategyPrism cms = method.getMapperConfiguration().getCollectionMappingStrategy();
return method.getResultType().getPropertyWriteAccessors( cms ).containsKey( name ); return method.getResultType().getPropertyWriteAccessors( cms ).containsKey( name );
@ -348,9 +329,7 @@ public class Mapping {
formattingParameters, formattingParameters,
selectionParameters, selectionParameters,
dependsOnAnnotationValue, dependsOnAnnotationValue,
Collections.<String>emptyList(), Collections.<String>emptyList()
valuePresenceCheckStrategy,
isSetValuePresenceCheckStrategy
); );
reverse.init( method, messager, typeFactory ); reverse.init( method, messager, typeFactory );
@ -377,9 +356,7 @@ public class Mapping {
formattingParameters, formattingParameters,
selectionParameters, selectionParameters,
dependsOnAnnotationValue, dependsOnAnnotationValue,
dependsOn, dependsOn
valuePresenceCheckStrategy,
isSetValuePresenceCheckStrategy
); );
if ( sourceReference != null ) { if ( sourceReference != null ) {

View File

@ -16,19 +16,16 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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 * @author Sean Huang
*/ */
public class MyObject { public enum NullValueCheckStrategy {
@Override
public boolean equals(Object object) {
return this == object;
}
@Override ON_IMPLICIT_CONVERSION,
public int hashCode() { ALLWAYS;
return super.hashCode();
}
} }

View File

@ -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<Class<?>, Object> SERVICES = new ConcurrentHashMap<Class<?>, Object>();
private Services() {
}
public static <T> T get(Class<T> serviceType, T defaultValue) {
@SuppressWarnings("unchecked")
T service = (T) SERVICES.get( serviceType );
if ( service == null ) {
service = loadAndCache( serviceType, defaultValue );
}
return service;
}
private static <T> T loadAndCache(Class<T> 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> T find(Class<T> 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;
}
}

View File

@ -35,11 +35,10 @@ import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror; import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements; 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.AfterMappingPrism;
import org.mapstruct.ap.internal.prism.BeforeMappingPrism; 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.AccessorNamingStrategy;
import org.mapstruct.ap.spi.DefaultAccessorNamingStrategy;
import org.mapstruct.ap.spi.MethodType; import org.mapstruct.ap.spi.MethodType;
/** /**
@ -63,8 +62,7 @@ public class Executables {
} }
private static final AccessorNamingStrategy ACCESSOR_NAMING_STRATEGY = Services.get( private static final AccessorNamingStrategy ACCESSOR_NAMING_STRATEGY = Services.get(
AccessorNamingStrategy.class, AccessorNamingStrategy.class, new DefaultAccessorNamingStrategy()
new DefaultAccessorNamingStrategy()
); );
private Executables() { private Executables() {
@ -98,8 +96,8 @@ public class Executables {
return method.getModifiers().contains( Modifier.PUBLIC ); return method.getModifiers().contains( Modifier.PUBLIC );
} }
public static String getPropertyName(ExecutableElement getterOrHasserOrSetterMethod) { public static String getPropertyName(ExecutableElement getterOrPresenceCheckerOrSetterMethod) {
return ACCESSOR_NAMING_STRATEGY.getPropertyName( getterOrHasserOrSetterMethod ); return ACCESSOR_NAMING_STRATEGY.getPropertyName( getterOrPresenceCheckerOrSetterMethod );
} }
public static boolean isDefaultMethod(ExecutableElement method) { public static boolean isDefaultMethod(ExecutableElement method) {

View File

@ -32,7 +32,7 @@ import org.mapstruct.ap.internal.prism.MapperConfigPrism;
import org.mapstruct.ap.internal.prism.MapperPrism; import org.mapstruct.ap.internal.prism.MapperPrism;
import org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism; import org.mapstruct.ap.internal.prism.MappingInheritanceStrategyPrism;
import org.mapstruct.ap.internal.prism.NullValueMappingStrategyPrism; 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 * 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) { public boolean isMapToDefault(NullValueMappingStrategyPrism mapNullToDefault) {
// check on method level // check on method level
@ -185,21 +194,4 @@ public class MapperConfiguration {
return mapperPrism.mirror; 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;
}
}
} }

View File

@ -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> T get(Class<T> serviceType, T defaultValue) {
Iterator<T> 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;
}
}

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.mapstruct.ap.internal.naming; package org.mapstruct.ap.spi;
import java.beans.Introspector; 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.SimpleElementVisitor6;
import javax.lang.model.util.SimpleTypeVisitor6; 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. * The default JavaBeans-compliant implementation of the {@link AccessorNamingStrategy} service provider interface.
@ -43,9 +41,6 @@ public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy {
if ( isGetterMethod( method ) ) { if ( isGetterMethod( method ) ) {
return MethodType.GETTER; return MethodType.GETTER;
} }
else if ( isPresenceCheckMethod( method ) ) {
return MethodType.PRESENCE_CHECKER;
}
else if ( isSetterMethod( method ) ) { else if ( isSetterMethod( method ) ) {
return MethodType.SETTER; return MethodType.SETTER;
} }
@ -70,14 +65,6 @@ public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy {
return isNonBooleanGetterName || ( isBooleanGetterName && returnTypeIsBoolean ); 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) { public boolean isSetterMethod(ExecutableElement method) {
String methodName = method.getSimpleName().toString(); String methodName = method.getSimpleName().toString();
@ -92,8 +79,8 @@ public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy {
@Override @Override
public String getPropertyName(ExecutableElement getterOrHasserOrSetterMethod) { public String getPropertyName(ExecutableElement getterOrSetterMethod) {
String methodName = getterOrHasserOrSetterMethod.getSimpleName().toString(); String methodName = getterOrSetterMethod.getSimpleName().toString();
return Introspector.decapitalize( methodName.substring( methodName.startsWith( "is" ) ? 2 : 3 ) ); 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 ); 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( DeclaredType declaredType = type.accept(
new SimpleTypeVisitor6<DeclaredType, Void>() { new SimpleTypeVisitor6<DeclaredType, Void>() {
@Override @Override

View File

@ -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() ) ) );
}
}

View File

@ -23,15 +23,23 @@
if ( ${sourceParameter.name} == null ) { if ( ${sourceParameter.name} == null ) {
return ${returnType.null}; return ${returnType.null};
} }
<#list propertyEntries as entry> <#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}()</#if>; <#if entry.presenceChecker?? >
if ( !<@localVarName index=entry_index/>.${entry.presenceChecker.simpleName}() ) {
return ${returnType.null};
}
</#if>
<@includeModel object=entry.type/> ${entry.name} = <@localVarName index=entry_index/>.${entry.accessorName}();
<#if !entry.presenceChecker?? >
<#if !entry.type.primitive> <#if !entry.type.primitive>
if ( ${entry.name} == null ) { if ( ${entry.name} == null ) {
return ${returnType.null}; return ${returnType.null};
} }
</#if> </#if>
</#if>
<#if !entry_has_next> <#if !entry_has_next>
return ${entry.name}; return ${entry.name};
</#if> </#if>
</#list> </#list>
} }
<#macro localVarName index><#if index == 0>${sourceParameter.name}<#else>${propertyEntries[index-1].name}</#if></#macro>

View File

@ -18,7 +18,7 @@
limitations under the License. limitations under the License.
--> -->
if ( ${sourceReference} != null ) { if ( <#if sourcePresenceChecker?? >${sourcePresenceChecker}<#else>${sourceReference} != null</#if> ) {
<@includeModel object=assignment <@includeModel object=assignment
targetBeanName=ext.targetBeanName targetBeanName=ext.targetBeanName
existingInstanceMapping=ext.existingInstanceMapping existingInstanceMapping=ext.existingInstanceMapping

View File

@ -18,7 +18,7 @@
limitations under the License. limitations under the License.
--> -->
if ( ${sourceReference} != null ) { if ( <#if sourcePresenceChecker?? >${sourcePresenceChecker}<#else>${sourceReference} != null</#if> ) {
<@includeModel object=assignment <@includeModel object=assignment
targetBeanName=ext.targetBeanName targetBeanName=ext.targetBeanName
existingInstanceMapping=ext.existingInstanceMapping existingInstanceMapping=ext.existingInstanceMapping

View File

@ -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<String> list = new ArrayList<String>();
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<String> list = new ArrayList<String>();
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<String> list = new ArrayList<String>();
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 ) );
}
}
}

View File

@ -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);
*/
}

View File

@ -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 );
}
}

View File

@ -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 );
}
}

View File

@ -16,30 +16,15 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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;
} }

View File

@ -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 );
}
}

View File

@ -16,30 +16,28 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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; public static final RockFestivalMapperWithConfig INSTANCE =
private String noCheckObject; Mappers.getMapper( RockFestivalMapperWithConfig.class );
public int getNoCheckPrimitive() { @Mapping( target = "stage", source = "artistName" )
return noCheckPrimitive; 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;
}
} }

View File

@ -16,20 +16,22 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.mapstruct.ap.test.presencecheck; package org.mapstruct.ap.test.source.nullvaluecheckstrategy;
import org.mapstruct.Mapper;
import org.mapstruct.SourceValuePresenceCheckStrategy;
/** /**
* @author Sean Huang *
* @author Sjaak Derksen
*/ */
@Mapper( sourceValuePresenceCheckStrategy = SourceValuePresenceCheckStrategy.IS_NULL_INLINE ) public class RockFestivalSource {
public class CustomMapper {
public MyLongWrapper toMyLongWrapperViaPrimitive(Long primitive) { private String artistName;
MyLongWrapper wrapper = new MyLongWrapper();
wrapper.setMyLong( primitive ); public String getArtistName() {
return wrapper; return artistName;
} }
public void setArtistName(String artistName) {
this.artistName = artistName;
}
} }

View File

@ -16,23 +16,22 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.mapstruct.ap.test.presencecheck; package org.mapstruct.ap.test.source.nullvaluecheckstrategy;
/** /**
*
* @author Sjaak Derksen * @author Sjaak Derksen
*/ */
public class MyLongWrapper { public class RockFestivalTarget {
private Long myLong; private Stage stage;
public Long getMyLong() { public Stage getStage() {
return myLong; return stage;
} }
public void setMyLong(Long myLong) { public void setStage(Stage stage) {
myLong.longValue(); this.stage = stage;
this.myLong = myLong;
} }
} }

View File

@ -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<String> 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;
}
}

View File

@ -16,30 +16,31 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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 name;
private String noCheckObject; private boolean hasName = true;
public int getNoCheckPrimitive() { public String getName() {
return noCheckPrimitive; return name;
} }
public void setNoCheckPrimitive(int noCheckPrimitive) { public void setName(String name) {
this.noCheckPrimitive = noCheckPrimitive; this.name = name;
} }
public String getNoCheckObject() { public boolean hasName() {
return noCheckObject; return hasName;
} }
public void setNoCheckObject(String noCheckObject) { public void setHasName(boolean hasName) {
this.noCheckObject = noCheckObject; this.hasName = hasName;
} }
} }

View File

@ -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();
}
}

View File

@ -16,30 +16,31 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * 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 { @Mapper( collectionMappingStrategy = CollectionMappingStrategy.ADDER_PREFERRED )
/** public interface SoccerTeamMapper {
* Only check != null for inline conversions
*
*/
IS_NULL_INLINE,
/** SoccerTeamMapper INSTANCE = Mappers.getMapper( SoccerTeamMapper.class );
* Always check != null, no matter whether it's an inline or method conversion
* @Mapping( target = "goalKeeperName", ignore = true )
*/ SoccerTeamTarget mapAdder( SoccerTeamSource in );
IS_NULL,
@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;
} }

View File

@ -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<String> 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<String> getPlayers() {
return players;
}
public void setPlayers(List<String> 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;
}
}

View File

@ -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<String> players;
private String goalKeeperName;
public List<String> getPlayers() {
return players;
}
public void addPlayer(String player) {
if ( this.players == null ) {
this.players = new ArrayList<String>();
}
this.players.add( player );
}
public String getGoalKeeperName() {
return goalKeeperName;
}
public void setGoalKeeperName(String goalKeeperName) {
this.goalKeeperName = goalKeeperName;
}
}

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.mapstruct.ap.test.presencecheck; package org.mapstruct.ap.test.source.presencecheck.spi;
import java.util.List; import java.util.List;
@ -25,39 +25,17 @@ import java.util.List;
*/ */
public class Source { public class Source {
private MyObject someObject;
private boolean hasSomeObject = true;
private double somePrimitiveDouble; private double somePrimitiveDouble;
private boolean hasPrimitiveSomeDouble = true; private boolean hasPrimitiveSomeDouble = true;
private Integer someInteger; private Integer someInteger;
private boolean hasSomeInteger = true; private boolean hasSomeInteger = true;
private Long someLong1;
private boolean hasSomeLong1 = true;
private Long someLong2;
private boolean hasSomeLong2 = true;
private List<String> someList; private List<String> someList;
private boolean hasSomeList = true; private boolean hasSomeList = true;
public boolean hasSomeObject() { private String[] someArray;
return hasSomeObject; private boolean hasSomeArray = true;
}
public void setHasSomeObject(boolean has) {
this.hasSomeObject = has;
}
public MyObject getSomeObject() {
return someObject;
}
public void setSomeObject(MyObject someObject) {
this.someObject = someObject;
}
public boolean hasSomePrimitiveDouble() { public boolean hasSomePrimitiveDouble() {
return hasPrimitiveSomeDouble; return hasPrimitiveSomeDouble;
@ -91,38 +69,6 @@ public class Source {
this.someInteger = someInteger; 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() { public boolean hasSomeList() {
return hasSomeList; return hasSomeList;
} }
@ -138,4 +84,21 @@ public class Source {
public void setSomeList(List<String> someList) { public void setSomeList(List<String> someList) {
this.someList = 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;
}
} }

View File

@ -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<String> toList( String in ) {
return Arrays.asList( in.split( "," ) );
}
protected String[] toArray( String in ) {
return in.split( "," );
}
}

View File

@ -16,7 +16,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.mapstruct.ap.test.presencecheck; package org.mapstruct.ap.test.source.presencecheck.spi;
import java.util.List; import java.util.List;
@ -25,20 +25,10 @@ import java.util.List;
*/ */
public class Target { public class Target {
private MyObject someObject;
private double somePrimitiveDouble; private double somePrimitiveDouble;
private Integer someInteger; private Integer someInteger;
private MyLongWrapper someLong1;
private MyLongWrapper someLong2;
private List<String> someList; private List<String> someList;
private String[] someArray;
public MyObject getSomeObject() {
return someObject;
}
public void setSomeObject(MyObject someObject) {
this.someObject = someObject;
}
public double getSomePrimitiveDouble() { public double getSomePrimitiveDouble() {
return somePrimitiveDouble; return somePrimitiveDouble;
@ -56,22 +46,6 @@ public class Target {
this.someInteger = someInteger; 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<String> getSomeList() { public List<String> getSomeList() {
return someList; return someList;
} }
@ -79,4 +53,13 @@ public class Target {
public void setSomeList(List<String> someList) { public void setSomeList(List<String> someList) {
this.someList = someList; this.someList = someList;
} }
public String[] getSomeArray() {
return someArray;
}
public void setSomeArray(String[] someArray) {
this.someArray = someArray;
}
} }