mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
issue 92 solution for list getter without setter
This commit is contained in:
parent
3e34b6f6eb
commit
e5cfb07af5
@ -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>();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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(
|
||||
|
@ -150,4 +150,5 @@ public class Executables {
|
||||
public Type retrieveReturnType(ExecutableElement method) {
|
||||
return typeFactory.getType( method.getReturnType() );
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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}() );
|
||||
|
@ -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() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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" ) );
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user