#669 ispresentcheck and NULL check implementation

This commit is contained in:
seanjob 2016-01-14 11:46:43 -05:00 committed by sjaakd
parent c47fd95d04
commit 1af4441d2d
26 changed files with 951 additions and 38 deletions

View File

@ -18,6 +18,8 @@
*/
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;
@ -148,4 +150,12 @@ public @interface Mapper {
* specified with {@link #config()}.
*/
MappingInheritanceStrategy mappingInheritanceStrategy() default MappingInheritanceStrategy.EXPLICIT;
/**
* Decide how to do presence check, such as checking null or calling hasX method, before mapping.
* Can be overridden by the one on {@link MapperConfig} or {@link Mapping}.
*
* @return strategy about how to do null or presence check
*/
SourceValuePresenceCheckStrategy sourceValuePresenceCheckStrategy() default IS_NULL_INLINE;
}

View File

@ -18,6 +18,8 @@
*/
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;
@ -135,4 +137,12 @@ public @interface MapperConfig {
*/
MappingInheritanceStrategy mappingInheritanceStrategy()
default MappingInheritanceStrategy.EXPLICIT;
/**
* Decide how to do presence check, such as checking null or calling hasXXX method, before mapping.
* Can be overridden by the one on {@link Mapper} or {@link Mapping}.
*
* @return strategy about how to do null or presence check
*/
SourceValuePresenceCheckStrategy sourceValuePresenceCheckStrategy() default IS_NULL_INLINE;
}

View File

@ -0,0 +1,45 @@
/**
* 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 to decide how to check null or hasX method before mapping
*
* @author Sean Huang
*/
public enum SourceValuePresenceCheckStrategy {
/**
* 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

@ -18,6 +18,8 @@
*/
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;
@ -191,4 +193,12 @@ 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;
}

View File

@ -18,6 +18,8 @@
*/
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;
@ -190,4 +192,12 @@ 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;
}

View File

@ -367,9 +367,6 @@ public class BeanMappingMethod extends MappingMethod {
Iterator<Entry<String, ExecutableElement>> targetPropertiesIterator =
unprocessedTargetProperties.entrySet().iterator();
// usually there should be only one getter; only for Boolean there may be two: isFoo() and getFoo()
List<ExecutableElement> candidates = new ArrayList<ExecutableElement>( 2 );
while ( targetPropertiesIterator.hasNext() ) {
Entry<String, ExecutableElement> targetProperty = targetPropertiesIterator.next();
@ -387,18 +384,24 @@ public class BeanMappingMethod extends MappingMethod {
continue;
}
PropertyMapping newPropertyMapping = null;
ExecutableElement sourceAccessor = sourceType.getPropertyReadAccessors().get( propertyName );
if ( sourceAccessor != null ) {
Mapping mapping = method.getSingleMappingByTargetPropertyName( propertyName );
ExecutableElement sourceReadAccessor =
sourceParameter.getType().getPropertyReadAccessors().get( propertyName );
ExecutableElement sourcePresenceChecker =
sourceParameter.getType().getPropertyPresenceCheckers().get( propertyName );
if ( sourceReadAccessor != null ) {
Mapping mapping = method.getSingleMappingByTargetPropertyName( targetProperty.getKey() );
DeclaredType declaredSourceType = (DeclaredType) sourceParameter.getType().getTypeMirror();
SourceReference sourceRef = new SourceReference.BuilderFromProperty()
.sourceParameter( sourceParameter )
.type( ctx.getTypeFactory().getReturnType( declaredSourceType, sourceAccessor ) )
.accessor( sourceAccessor )
.name( propertyName )
.type( ctx.getTypeFactory().getReturnType( declaredSourceType, sourceReadAccessor ) )
.readAccessor( sourceReadAccessor )
.presenceChecker( sourcePresenceChecker )
.name( targetProperty.getKey() )
.build();
newPropertyMapping = new PropertyMappingBuilder()
@ -416,10 +419,7 @@ public class BeanMappingMethod extends MappingMethod {
.build();
unprocessedSourceParameters.remove( sourceParameter );
}
// candidates are handled
candidates.clear();
if ( propertyMapping != null && newPropertyMapping != null ) {
// TODO improve error message

View File

@ -145,7 +145,7 @@ public class NestedPropertyMappingMethod extends MappingMethod {
private final String safeName;
public SafePropertyEntry( PropertyEntry entry, String safeName ) {
super( entry.getName(), entry.getAccessor(), entry.getType() );
super( entry.getName(), entry.getAccessor(), entry.getPresenceChecker(), entry.getType() );
this.safeName = safeName;
}

View File

@ -85,6 +85,7 @@ public class Type extends ModelElement implements Comparable<Type> {
private final List<String> enumConstants;
private Map<String, ExecutableElement> getters = null;
private Map<String, ExecutableElement> presenceCheckers = null;
private List<ExecutableElement> allExecutables = null;
private List<ExecutableElement> setters = null;
@ -380,6 +381,23 @@ public class Type extends ModelElement implements Comparable<Type> {
return getters;
}
/**
* getPropertyPresenceCheckers
*
* @return an unmodifiable map of all presence checkers, indexed by property name
*/
public Map<String, ExecutableElement> getPropertyPresenceCheckers() {
if ( presenceCheckers == null ) {
List<ExecutableElement> checkerList = Filters.presenceCheckMethodsIn( getAllExecutables() );
Map<String, ExecutableElement> modifiableCheckers = new LinkedHashMap<String, ExecutableElement>();
for (ExecutableElement checker : checkerList) {
modifiableCheckers.put( Executables.getPropertyName( checker ), checker );
}
presenceCheckers = Collections.unmodifiableMap( modifiableCheckers );
}
return presenceCheckers;
}
/**
* getPropertyWriteAccessors returns a map of the write accessors according to the CollectionMappingStrategy. These
* accessors include:

View File

@ -38,6 +38,7 @@ 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;
@ -61,6 +62,9 @@ public class Mapping {
private final boolean isIgnored;
private final List<String> dependsOn;
private final SourceValuePresenceCheckStrategy valuePresenceCheckStrategy;
private final boolean isSetValuePresenceCheckStrategy;
private final AnnotationMirror mirror;
private final AnnotationValue sourceAnnotationValue;
private final AnnotationValue targetAnnotationValue;
@ -96,7 +100,6 @@ public class Mapping {
public static Mapping fromMappingPrism(MappingPrism mappingPrism, ExecutableElement element,
FormattingMessager messager) {
if ( mappingPrism.target().isEmpty() ) {
messager.printMessage(
element,
@ -139,13 +142,14 @@ public class Mapping {
List<String> dependsOn =
mappingPrism.dependsOn() != null ? mappingPrism.dependsOn() : Collections.<String>emptyList();
boolean isSetValuePresenceCheckStrategy = mappingPrism.values.sourceValuePresenceCheckStrategy() != null;
FormattingParameters formattingParam = new FormattingParameters( dateFormat, numberFormat );
SelectionParameters selectionParams = new SelectionParameters(
mappingPrism.qualifiedBy(),
mappingPrism.qualifiedByName(),
resultTypeIsDefined ? mappingPrism.resultType() : null);
FormattingParameters formattingParam = new FormattingParameters( dateFormat, numberFormat );
return new Mapping(
source,
constant,
@ -159,16 +163,20 @@ public class Mapping {
formattingParam,
selectionParams,
mappingPrism.values.dependsOn(),
dependsOn
dependsOn,
SourceValuePresenceCheckStrategy.valueOf( mappingPrism.sourceValuePresenceCheckStrategy() ),
isSetValuePresenceCheckStrategy
);
}
@SuppressWarnings("checkstyle:parameternumber")
private Mapping(String sourceName, String constant, String javaExpression, String targetName,
private Mapping( String sourceName, String constant, String javaExpression, String targetName,
String defaultValue, boolean isIgnored, AnnotationMirror mirror,
AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue,
FormattingParameters formattingParameters, SelectionParameters selectionParameters,
AnnotationValue dependsOnAnnotationValue, List<String> dependsOn) {
AnnotationValue dependsOnAnnotationValue, List<String> dependsOn,
SourceValuePresenceCheckStrategy valuePresenceCheckStrategy,
boolean isSetValuePresenceCheckStrategy ) {
this.sourceName = sourceName;
this.constant = constant;
this.javaExpression = javaExpression;
@ -182,6 +190,8 @@ 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,
@ -282,6 +292,14 @@ 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 );
@ -330,7 +348,9 @@ public class Mapping {
formattingParameters,
selectionParameters,
dependsOnAnnotationValue,
Collections.<String>emptyList()
Collections.<String>emptyList(),
valuePresenceCheckStrategy,
isSetValuePresenceCheckStrategy
);
reverse.init( method, messager, typeFactory );
@ -357,7 +377,9 @@ public class Mapping {
formattingParameters,
selectionParameters,
dependsOnAnnotationValue,
dependsOn
dependsOn,
valuePresenceCheckStrategy,
isSetValuePresenceCheckStrategy
);
if ( sourceReference != null ) {
@ -375,3 +397,4 @@ public class Mapping {
"\n}";
}
}

View File

@ -175,12 +175,14 @@ public class SourceReference {
for ( String entryName : entryNames ) {
boolean matchFound = false;
Map<String, ExecutableElement> sourceReadAccessors = newType.getPropertyReadAccessors();
Map<String, ExecutableElement> sourcePresenceCheckers = newType.getPropertyPresenceCheckers();
for ( Map.Entry<String, ExecutableElement> getter : sourceReadAccessors.entrySet() ) {
if ( getter.getKey().equals( entryName ) ) {
newType = typeFactory.getReturnType(
(DeclaredType) newType.getTypeMirror(), getter.getValue()
);
sourceEntries.add( new PropertyEntry( entryName, getter.getValue(), newType ) );
newType = typeFactory.getReturnType( (DeclaredType) newType.getTypeMirror(),
getter.getValue() );
sourceEntries.add( new PropertyEntry( entryName, getter.getValue(),
sourcePresenceCheckers.get( entryName ), newType ) );
matchFound = true;
break;
}
@ -204,7 +206,8 @@ public class SourceReference {
public static class BuilderFromProperty {
private String name;
private ExecutableElement accessor;
private ExecutableElement readAccessor;
private ExecutableElement presenceChecker;
private Type type;
private Parameter sourceParameter;
@ -213,8 +216,13 @@ public class SourceReference {
return this;
}
public BuilderFromProperty accessor(ExecutableElement accessor) {
this.accessor = accessor;
public BuilderFromProperty readAccessor(ExecutableElement readAccessor) {
this.readAccessor = readAccessor;
return this;
}
public BuilderFromProperty presenceChecker(ExecutableElement presenceChecker) {
this.presenceChecker = presenceChecker;
return this;
}
@ -230,8 +238,8 @@ public class SourceReference {
public SourceReference build() {
List<PropertyEntry> sourcePropertyEntries = new ArrayList<PropertyEntry>();
if ( accessor != null ) {
sourcePropertyEntries.add( new PropertyEntry( name, accessor, type ) );
if ( readAccessor != null ) {
sourcePropertyEntries.add( new PropertyEntry( name, readAccessor, presenceChecker, type ) );
}
return new SourceReference( sourceParameter, sourcePropertyEntries, true );
}
@ -271,11 +279,14 @@ public class SourceReference {
private final String name;
private final ExecutableElement accessor;
private final ExecutableElement presenceChecker;
private final Type type;
public PropertyEntry(String name, ExecutableElement accessor, Type type) {
public PropertyEntry(String name, ExecutableElement readAccessor,
ExecutableElement presenceChecker, Type type) {
this.name = name;
this.accessor = accessor;
this.accessor = readAccessor;
this.presenceChecker = presenceChecker;
this.type = type;
}
@ -287,6 +298,10 @@ public class SourceReference {
return accessor;
}
public ExecutableElement getPresenceChecker() {
return presenceChecker;
}
public Type getType() {
return type;
}

View File

@ -43,6 +43,9 @@ 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;
}
@ -67,6 +70,14 @@ 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();
@ -81,8 +92,8 @@ public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy {
@Override
public String getPropertyName(ExecutableElement getterOrSetterMethod) {
String methodName = getterOrSetterMethod.getSimpleName().toString();
public String getPropertyName(ExecutableElement getterOrHasserOrSetterMethod) {
String methodName = getterOrHasserOrSetterMethod.getSimpleName().toString();
return Introspector.decapitalize( methodName.substring( methodName.startsWith( "is" ) ? 2 : 3 ) );
}

View File

@ -0,0 +1,45 @@
/**
* 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.prism;
/**
* Prism for the enum {@link org.mapstruct.SourceValuePresenceCheckStrategy}
*
* @author Sean Huang
*/
public enum SourceValuePresenceCheckStrategy {
/**
* 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

@ -76,6 +76,12 @@ public class Executables {
ACCESSOR_NAMING_STRATEGY.getMethodType( method ) == MethodType.GETTER;
}
public static boolean isPresenceCheckMethod(ExecutableElement method) {
return isPublic( method ) &&
method.getParameters().isEmpty() &&
ACCESSOR_NAMING_STRATEGY.getMethodType( method ) == MethodType.PRESENCE_CHECKER;
}
public static boolean isSetterMethod(ExecutableElement method) {
return isPublic( method )
&& method.getParameters().size() == 1
@ -92,8 +98,8 @@ public class Executables {
return method.getModifiers().contains( Modifier.PUBLIC );
}
public static String getPropertyName(ExecutableElement getterOrSetterMethod) {
return ACCESSOR_NAMING_STRATEGY.getPropertyName( getterOrSetterMethod );
public static String getPropertyName(ExecutableElement getterOrHasserOrSetterMethod) {
return ACCESSOR_NAMING_STRATEGY.getPropertyName( getterOrHasserOrSetterMethod );
}
public static boolean isDefaultMethod(ExecutableElement method) {

View File

@ -46,6 +46,18 @@ public class Filters {
return getterMethods;
}
public static List<ExecutableElement> presenceCheckMethodsIn(Iterable<ExecutableElement> elements) {
List<ExecutableElement> presenceCheckMethods = new LinkedList<ExecutableElement>();
for ( ExecutableElement method : elements ) {
if ( Executables.isPresenceCheckMethod( method ) ) {
presenceCheckMethods.add( method );
}
}
return presenceCheckMethods;
}
public static List<ExecutableElement> setterMethodsIn(Iterable<ExecutableElement> elements) {
List<ExecutableElement> setterMethods = new LinkedList<ExecutableElement>();

View File

@ -32,6 +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;
/**
* Provides an aggregated view to the settings given via {@link org.mapstruct.Mapper} and
@ -183,4 +184,22 @@ public class MapperConfiguration {
public AnnotationMirror getAnnotationMirror() {
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

@ -51,6 +51,7 @@ public enum Message {
PROPERTYMAPPING_INVALID_PARAMETER_NAME( "Method has no parameter named \"%s\"." ),
PROPERTYMAPPING_NO_PROPERTY_IN_PARAMETER( "The type of parameter \"%s\" has no property named \"%s\"." ),
PROPERTYMAPPING_INVALID_PROPERTY_NAME( "No property named \"%s\" exists in source parameter(s)." ),
PROPERTYMAPPING_NO_PRESENCE_CHECKER_FOR_SOURCE_TYPE( "Using custom source value presence checking strategy, but no presence checker found for %s in source type." ),
PROPERTYMAPPING_NO_READ_ACCESSOR_FOR_TARGET_TYPE( "No read accessor found for property \"%s\" in target type." ),
CONSTANTMAPPING_MAPPING_NOT_FOUND( "Can't map \"%s %s\" to \"%s %s\"." ),

View File

@ -43,5 +43,10 @@ public enum MethodType {
/**
* Any method which is neither a JavaBeans getter, setter nor an adder method.
*/
OTHER;
OTHER,
/**
* A method to check whether a property is present, e.g. {@code public String hasName()}.
*/
PRESENCE_CHECKER;
}

View File

@ -0,0 +1,35 @@
/**
* 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 org.mapstruct.Mapper;
import org.mapstruct.SourceValuePresenceCheckStrategy;
/**
* @author Sean Huang
*/
@Mapper( sourceValuePresenceCheckStrategy = SourceValuePresenceCheckStrategy.IS_NULL_INLINE )
public class CustomMapper {
public MyLongWrapper toMyLongWrapperViaPrimitive(Long primitive) {
MyLongWrapper wrapper = new MyLongWrapper();
wrapper.setMyLong( primitive );
return wrapper;
}
}

View File

@ -0,0 +1,38 @@
/**
* 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;
/**
* @author Sjaak Derksen
*/
public class MyLongWrapper {
private Long myLong;
public Long getMyLong() {
return myLong;
}
public void setMyLong(Long myLong) {
myLong.longValue();
this.myLong = myLong;
}
}

View File

@ -0,0 +1,34 @@
/**
* 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;
/**
* @author Sean Huang
*/
public class MyObject {
@Override
public boolean equals(Object object) {
return this == object;
}
@Override
public int hashCode() {
return super.hashCode();
}
}

View File

@ -0,0 +1,171 @@
/**
* 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

@ -0,0 +1,141 @@
/**
* 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.List;
/**
* @author Sean Huang
*/
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<String> 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;
}
public boolean hasSomePrimitiveDouble() {
return hasPrimitiveSomeDouble;
}
public void setHasSomePrimitiveDouble(boolean has) {
this.hasPrimitiveSomeDouble = has;
}
public double getSomePrimitiveDouble() {
return somePrimitiveDouble;
}
public void setSomePrimitiveDouble(double someDouble) {
this.somePrimitiveDouble = someDouble;
}
public boolean hasSomeInteger() {
return hasSomeInteger;
}
public void setHasSomeInteger(boolean hasSomeInteger) {
this.hasSomeInteger = hasSomeInteger;
}
public Integer getSomeInteger() {
return someInteger;
}
public void setSomeInteger(Integer 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() {
return hasSomeList;
}
public void setHasSomeList(boolean has) {
this.hasSomeList = has;
}
public List<String> getSomeList() {
return someList;
}
public void setSomeList(List<String> someList) {
this.someList = someList;
}
}

View File

@ -0,0 +1,82 @@
/**
* 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,45 @@
/**
* 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;
/**
* @author Sean Huang
*/
public class SourceWtCheck extends Source {
private int noCheckPrimitive;
private String noCheckObject;
public int getNoCheckPrimitive() {
return noCheckPrimitive;
}
public void setNoCheckPrimitive(int noCheckPrimitive) {
this.noCheckPrimitive = noCheckPrimitive;
}
public String getNoCheckObject() {
return noCheckObject;
}
public void setNoCheckObject(String noCheckObject) {
this.noCheckObject = noCheckObject;
}
}

View File

@ -0,0 +1,82 @@
/**
* 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.List;
/**
* @author Sean Huang
*/
public class Target {
private MyObject someObject;
private double somePrimitiveDouble;
private Integer someInteger;
private MyLongWrapper someLong1;
private MyLongWrapper someLong2;
private List<String> someList;
public MyObject getSomeObject() {
return someObject;
}
public void setSomeObject(MyObject someObject) {
this.someObject = someObject;
}
public double getSomePrimitiveDouble() {
return somePrimitiveDouble;
}
public void setSomePrimitiveDouble(double someDouble) {
this.somePrimitiveDouble = someDouble;
}
public Integer getSomeInteger() {
return someInteger;
}
public void setSomeInteger(Integer 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() {
return someList;
}
public void setSomeList(List<String> someList) {
this.someList = someList;
}
}

View File

@ -0,0 +1,45 @@
/**
* 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;
/**
* @author Sean Huang
*/
public class TargetWtCheck extends Target {
private int noCheckPrimitive;
private String noCheckObject;
public int getNoCheckPrimitive() {
return noCheckPrimitive;
}
public void setNoCheckPrimitive(int noCheckPrimitive) {
this.noCheckPrimitive = noCheckPrimitive;
}
public String getNoCheckObject() {
return noCheckObject;
}
public void setNoCheckObject(String noCheckObject) {
this.noCheckObject = noCheckObject;
}
}