#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,7 +163,9 @@ public class Mapping {
formattingParam,
selectionParams,
mappingPrism.values.dependsOn(),
dependsOn
dependsOn,
SourceValuePresenceCheckStrategy.valueOf( mappingPrism.sourceValuePresenceCheckStrategy() ),
isSetValuePresenceCheckStrategy
);
}
@ -168,7 +174,9 @@ public class Mapping {
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;
}
}