issue 92 solution for list getter without setter

This commit is contained in:
sjaakd 2014-01-13 11:23:35 +01:00 committed by Gunnar Morling
parent 3e34b6f6eb
commit e5cfb07af5
15 changed files with 189 additions and 4 deletions

View File

@ -39,6 +39,7 @@ public class PropertyMapping extends AbstractModelElement {
private final String targetName;
private final String targetAccessorName;
private final Type targetType;
private final boolean isHasTargetSetter;
private final MappingMethodReference mappingMethod;
private final TypeConversion conversion;
@ -55,6 +56,7 @@ public class PropertyMapping extends AbstractModelElement {
this.targetName = targetName;
this.targetAccessorName = targetAccessorName;
this.targetType = targetType;
this.isHasTargetSetter = targetAccessorName.startsWith( "set" );
this.mappingMethod = mappingMethod;
this.conversion = conversion;
@ -96,6 +98,10 @@ public class PropertyMapping extends AbstractModelElement {
return conversion;
}
public boolean isHasTargetSetter() {
return isHasTargetSetter;
}
@Override
public Set<Type> getImportTypes() {
Set<Type> importTypes = new HashSet<Type>();

View File

@ -53,6 +53,7 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
private final boolean isEnumType;
private final boolean isIterableType;
private final boolean isCollectionType;
private final boolean isListType;
private final boolean isMapType;
private final Type implementationType;
private final TypeMirror typeMirror;
@ -60,12 +61,14 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
private final TypeElement typeElement;
public Type(TypeMirror typeMirror, List<Type> typeParameters, Type implementationType, boolean isIterableType,
boolean isCollectionType, boolean isMapType, Types typeUtils, Elements elementUtils) {
boolean isCollectionType, boolean isListType, boolean isMapType, Types typeUtils,
Elements elementUtils) {
this.typeMirror = typeMirror;
this.implementationType = implementationType;
this.typeParameters = typeParameters;
this.isIterableType = isIterableType;
this.isCollectionType = isCollectionType;
this.isListType = isListType;
this.isMapType = isMapType;
this.typeUtils = typeUtils;
@ -149,6 +152,10 @@ public class Type extends AbstractModelElement implements Comparable<Type> {
return isCollectionType;
}
public boolean isListType() {
return isListType;
}
public boolean isMapType() {
return isMapType;
}

View File

@ -472,14 +472,23 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Metho
ExecutableElement getterMethod, ExecutableElement setterMethod,
String dateFormat) {
Type sourceType = executables.retrieveReturnType( getterMethod );
Type targetType = executables.retrieveSingleParameter( setterMethod ).getType();
Type targetType = null;
String conversionStr = null;
if (executables.isSetterMethod( setterMethod ) ) {
targetType = executables.retrieveSingleParameter( setterMethod ).getType();
conversionStr = parameter.getName() + "." + getterMethod.getSimpleName().toString() + "()";
}
else if ( executables.isGetterMethod( setterMethod ) ) {
targetType = executables.retrieveReturnType( setterMethod );
conversionStr = parameter.getName() + "." + getterMethod.getSimpleName().toString() + "()";
}
MappingMethodReference propertyMappingMethod = getMappingMethodReference( methods, sourceType, targetType );
TypeConversion conversion = getConversion(
sourceType,
targetType,
dateFormat,
parameter.getName() + "." + getterMethod.getSimpleName().toString() + "()"
conversionStr
);
PropertyMapping property = new PropertyMapping(

View File

@ -150,4 +150,5 @@ public class Executables {
public Type retrieveReturnType(ExecutableElement method) {
return typeFactory.getType( method.getReturnType() );
}
}

View File

@ -52,11 +52,35 @@ public class Filters {
public List<ExecutableElement> setterMethodsIn(Iterable<? extends Element> elements) {
List<ExecutableElement> setterMethods = new LinkedList<ExecutableElement>();
List<ExecutableElement> getterMethods = new LinkedList<ExecutableElement>();
for ( ExecutableElement method : methodsIn( elements ) ) {
if ( executables.isSetterMethod( method ) ) {
setterMethods.add( method );
}
else if ( executables.isGetterMethod( method ) ) {
getterMethods.add( method );
}
}
if (getterMethods.size() > setterMethods.size()) {
// there could be a getter method for a list that is not present as setter.
// a getter could substitue the setter in that case and act as setter.
// (asuming its intitialized)
for ( ExecutableElement getterMethod : getterMethods ) {
boolean matchFound = false;
String getterPropertyName = executables.getPropertyName( getterMethod );
for ( ExecutableElement setterMethod : setterMethods ) {
String setterPropertyName = executables.getPropertyName( setterMethod );
if ( getterPropertyName.equals( setterPropertyName ) ) {
matchFound = true;
break;
}
}
if ( !matchFound && executables.retrieveReturnType( getterMethod ).isListType() ) {
setterMethods.add( getterMethod );
}
}
}
return setterMethods;

View File

@ -57,6 +57,7 @@ public class TypeFactory {
private final TypeMirror iterableType;
private final TypeMirror collectionType;
private final TypeMirror listType;
private final TypeMirror mapType;
private final Map<String, Type> implementationTypes = new HashMap<String, Type>();
@ -70,6 +71,7 @@ public class TypeFactory {
elementUtils.getTypeElement( Collection.class.getCanonicalName() )
.asType()
);
listType = typeUtils.erasure( elementUtils.getTypeElement( List.class.getCanonicalName() ).asType() );
mapType = typeUtils.erasure( elementUtils.getTypeElement( Map.class.getCanonicalName() ).asType() );
implementationTypes.put( Iterable.class.getName(), getType( ArrayList.class ) );
@ -115,6 +117,10 @@ public class TypeFactory {
mirror,
collectionType
);
boolean isListType = typeUtils.isSubtype(
mirror,
listType
);
boolean isMapType = typeUtils.isSubtype(
mirror,
mapType
@ -126,6 +132,7 @@ public class TypeFactory {
implementationType,
isIterableType,
isCollectionType,
isListType,
isMapType,
typeUtils,
elementUtils
@ -181,6 +188,7 @@ public class TypeFactory {
null,
implementationType.isIterableType(),
implementationType.isCollectionType(),
implementationType.isListType(),
implementationType.isMapType(),
typeUtils,
elementUtils

View File

@ -20,7 +20,13 @@
-->
<#-- a) invoke mapping method -->
<#if mappingMethod??>
<#if hasTargetSetter>
${ext.targetBeanName}.${targetAccessorName}( <@includeModel object=mappingMethod input="${sourceBeanName}.${sourceAccessorName}()"/> );
<#else>
if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
${ext.targetBeanName}.${targetAccessorName}().addAll( <@includeModel object=mappingMethod input="${sourceBeanName}.${sourceAccessorName}()"/> );
}
</#if>
<#-- b) simple conversion -->
<#elseif conversion??>
<#if sourceType.primitive == false>
@ -34,7 +40,11 @@ if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
<#else>
<#if targetType.collectionType || targetType.mapType>
if ( ${sourceBeanName}.${sourceAccessorName}() != null ) {
<#if hasTargetSetter>
${ext.targetBeanName}.${targetAccessorName}( new <#if targetType.implementationType??><@includeModel object=targetType.implementationType/><#else><@includeModel object=targetType/></#if>( ${sourceBeanName}.${sourceAccessorName}() ) );
<#else>
${ext.targetBeanName}.${targetAccessorName}().addAll( new <#if targetType.implementationType??><@includeModel object=targetType.implementationType/><#else><@includeModel object=targetType/></#if>( ${sourceBeanName}.${sourceAccessorName}() ) );
</#if>
}
<#else>
${ext.targetBeanName}.${targetAccessorName}( ${sourceBeanName}.${sourceAccessorName}() );

View File

@ -70,6 +70,18 @@ public class CollectionMappingTest extends MapperTestBase {
assertThat( target.getStringList() ).containsExactly( "Bob", "Alice" );
}
@Test
@IssueKey("6")
public void shouldMapListWithoutSetter() {
Source source = new Source();
source.setStringList2( Arrays.asList( "Bob", "Alice" ) );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getStringListNoSetter() ).containsExactly( "Bob", "Alice" );
}
@Test
@IssueKey("6")
public void shouldReverseMapList() {

View File

@ -45,6 +45,8 @@ public class Source {
private Map<String, Long> stringLongMap;
private List<String> stringList2;
public List<String> getStringList() {
return stringList;
}
@ -124,4 +126,13 @@ public class Source {
public void setStringLongMap(Map<String, Long> stringLongMap) {
this.stringLongMap = stringLongMap;
}
public List<String> getStringList2() {
return stringList2;
}
public void setStringList2( List<String> stringList2 ) {
this.stringList2 = stringList2;
}
}

View File

@ -33,7 +33,8 @@ public interface SourceTargetMapper {
@Mappings({
@Mapping(source = "integerList", target = "integerCollection"),
@Mapping(source = "integerSet", target = "set"),
@Mapping(source = "anotherIntegerSet", target = "anotherStringSet")
@Mapping(source = "anotherIntegerSet", target = "anotherStringSet"),
@Mapping(source = "stringList2", target = "stringListNoSetter")
})
Target sourceToTarget(Source source);

View File

@ -43,6 +43,8 @@ public class Target {
private Map<String, Long> stringLongMap;
private List<String> stringListNoSetter;
@SuppressWarnings("rawtypes")
private Set set;
@ -127,4 +129,12 @@ public class Target {
public void setStringLongMap(Map<String, Long> stringLongMap) {
this.stringLongMap = stringLongMap;
}
public List<String> getStringListNoSetter() {
if ( stringListNoSetter == null ) {
stringListNoSetter = new ArrayList<String>();
}
return stringListNoSetter;
}
}

View File

@ -42,6 +42,8 @@ import org.mapstruct.ap.testutil.WithClasses;
import org.testng.annotations.Test;
@WithClasses({
Source.class,
Target.class,
SourceFoo.class,
TargetFoo.class,
SourceTargetMapper.class
@ -168,6 +170,17 @@ public class DefaultCollectionImplementationTest extends MapperTestBase {
assertResultList( target );
}
@Test
@IssueKey("6")
public void shouldUseDefaultImplementationForListWithoutSetter() {
Source source = new Source();
source.setFooList( createSourceFooList() );
Target target = SourceTargetMapper.INSTANCE.sourceToTarget( source );
assertThat( target ).isNotNull();
assertThat( target.getFooListNoSetter()).containsExactly( new TargetFoo( "Bob" ), new TargetFoo( "Alice" ) );
}
private void assertResultList(Iterable<TargetFoo> fooIterable) {
assertThat( fooIterable ).isNotNull();
assertThat( fooIterable ).containsOnly( new TargetFoo( "Bob" ), new TargetFoo( "Alice" ) );

View File

@ -0,0 +1,35 @@
/**
* Copyright 2012-2014 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.collection.defaultimplementation;
import java.util.List;
public class Source {
private List<SourceFoo> fooList;
public List<SourceFoo> getFooList() {
return fooList;
}
public void setFooList( List<SourceFoo> fooList ) {
this.fooList = fooList;
}
}

View File

@ -30,6 +30,7 @@ import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ConcurrentNavigableMap;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.factory.Mappers;
@ -38,6 +39,9 @@ public interface SourceTargetMapper {
SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class );
@Mapping(source = "fooList", target = "fooListNoSetter")
Target sourceToTarget(Source source);
TargetFoo sourceFooToTargetFoo(SourceFoo sourceFoo);
List<TargetFoo> sourceFoosToTargetFoos(List<SourceFoo> foos);

View File

@ -0,0 +1,34 @@
/**
* Copyright 2012-2014 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.collection.defaultimplementation;
import java.util.ArrayList;
import java.util.List;
public class Target {
private List<TargetFoo> fooListNoSetter;
public List<TargetFoo> getFooListNoSetter() {
if ( fooListNoSetter == null ) {
fooListNoSetter = new ArrayList<TargetFoo>();
}
return fooListNoSetter;
}
}