#707 Automatic generation of iterable method if element mapping method(s) are qualified

This commit is contained in:
sjaakd 2016-09-06 19:46:58 +02:00
parent 9996fc66ab
commit c8a9592bc9
13 changed files with 576 additions and 50 deletions

View File

@ -51,6 +51,7 @@ public class IterableMappingMethod extends MappingMethod {
private final boolean overridden;
private final boolean mapNullToDefault;
private final String loopVariableName;
private final SelectionParameters selectionParameters;
public static class Builder {
@ -124,6 +125,7 @@ public class IterableMappingMethod extends MappingMethod {
if ( method instanceof ForgedMethod ) {
ForgedMethod forgedMethod = (ForgedMethod) method;
forgedMethod.addThrownTypes( assignment.getThrownTypes() );
}
}
// target accessor is setter, so decorate assignment as setter
@ -157,7 +159,8 @@ public class IterableMappingMethod extends MappingMethod {
mapNullToDefault,
loopVariableName,
beforeMappingMethods,
afterMappingMethods );
afterMappingMethods,
selectionParameters );
}
}
@ -165,13 +168,15 @@ public class IterableMappingMethod extends MappingMethod {
private IterableMappingMethod(Method method, Assignment parameterAssignment, MethodReference factoryMethod,
boolean mapNullToDefault, String loopVariableName,
List<LifecycleCallbackMethodReference> beforeMappingReferences,
List<LifecycleCallbackMethodReference> afterMappingReferences) {
List<LifecycleCallbackMethodReference> afterMappingReferences,
SelectionParameters selectionParameters ) {
super( method, beforeMappingReferences, afterMappingReferences );
this.elementAssignment = parameterAssignment;
this.factoryMethod = factoryMethod;
this.overridden = method.overridesMethod();
this.mapNullToDefault = mapNullToDefault;
this.loopVariableName = loopVariableName;
this.selectionParameters = selectionParameters;
}
public Parameter getSourceParameter() {
@ -305,6 +310,15 @@ public class IterableMappingMethod extends MappingMethod {
}
}
if ( this.selectionParameters != null ) {
if ( !this.selectionParameters.equals( other.selectionParameters ) ) {
return false;
}
}
else if ( other.selectionParameters != null ) {
return false;
}
return true;
}

View File

@ -250,7 +250,12 @@ public class PropertyMapping extends ModelElement {
// No mapping found. Try to forge a mapping
if ( assignment == null ) {
assignment = forgeMapOrIterableMapping( sourceType, targetType, sourceRefStr, method.getExecutable() );
if ( (sourceType.isCollectionType() || sourceType.isArrayType()) && targetType.isIterableType() ) {
assignment = forgeIterableMapping( sourceType, targetType, sourceRefStr, method.getExecutable() );
}
else if ( sourceType.isMapType() && targetType.isMapType() ) {
assignment = forgeMapMapping( sourceType, targetType, sourceRefStr, method.getExecutable() );
}
}
if ( assignment != null ) {
@ -601,7 +606,7 @@ public class PropertyMapping extends ModelElement {
return sourcePresenceChecker;
}
private Assignment forgeMapOrIterableMapping(Type sourceType, Type targetType, String sourceReference,
private Assignment forgeIterableMapping(Type sourceType, Type targetType, String sourceReference,
ExecutableElement element) {
Assignment assignment = null;
@ -609,9 +614,6 @@ public class PropertyMapping extends ModelElement {
String name = getName( sourceType, targetType );
name = Strings.getSaveVariableName( name, ctx.getNamesOfMappingsToGenerate() );
if ( ( sourceType.isCollectionType() || sourceType.isArrayType() )
&& ( targetType.isIterableType() ) ) {
// copy mapper configuration from the source method, its the same mapper
MapperConfiguration config = method.getMapperConfiguration();
ForgedMethod methodRef = new ForgedMethod( name, sourceType, targetType, config, element );
@ -620,6 +622,7 @@ public class PropertyMapping extends ModelElement {
IterableMappingMethod iterableMappingMethod = builder
.mappingContext( ctx )
.method( methodRef )
.selectionParameters( selectionParameters )
.build();
if ( iterableMappingMethod != null ) {
@ -634,8 +637,17 @@ public class PropertyMapping extends ModelElement {
assignment = AssignmentFactory.createMethodReference( methodRef, null, targetType );
assignment.setAssignment( AssignmentFactory.createDirect( sourceReference ) );
}
return assignment;
}
else if ( sourceType.isMapType() && targetType.isMapType() ) {
private Assignment forgeMapMapping(Type sourceType, Type targetType, String sourceReference,
ExecutableElement element) {
Assignment assignment = null;
String name = getName( sourceType, targetType );
name = Strings.getSaveVariableName( name, ctx.getNamesOfMappingsToGenerate() );
// copy mapper configuration from the source method, its the same mapper
MapperConfiguration config = method.getMapperConfiguration();
@ -658,7 +670,7 @@ public class PropertyMapping extends ModelElement {
assignment = AssignmentFactory.createMethodReference( methodRef, null, targetType );
assignment.setAssignment( AssignmentFactory.createDirect( sourceReference ) );
}
}
return assignment;
}

View File

@ -64,5 +64,45 @@ public class SelectionParameters {
return resultType;
}
@Override
public int hashCode() {
int hash = 3;
hash = 97 * hash + (this.qualifiers != null ? this.qualifiers.hashCode() : 0);
hash = 97 * hash + (this.qualifyingNames != null ? this.qualifyingNames.hashCode() : 0);
hash = 97 * hash + (this.resultType != null ? this.resultType.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object obj) {
if ( this == obj ) {
return true;
}
if ( obj == null ) {
return false;
}
if ( getClass() != obj.getClass() ) {
return false;
}
final SelectionParameters other = (SelectionParameters) obj;
if ( !equals( this.qualifiers, other.qualifiers ) ) {
return false;
}
if ( !equals( this.qualifyingNames, other.qualifyingNames ) ) {
return false;
}
return equals( this.resultType, other.resultType );
}
private boolean equals(Object object1, Object object2) {
if ( object1 == null ) {
return (object2 == null);
}
else {
return object1.equals( object2 );
}
}
}

View File

@ -0,0 +1,37 @@
/**
* 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.selection.qualifier.iterable;
/**
*
* @author Sjaak Derksen
*/
public class CityDto extends TopologyFeatureDto {
private int population;
public int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
}

View File

@ -0,0 +1,37 @@
/**
* 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.selection.qualifier.iterable;
/**
*
* @author Sjaak Derksen
*/
public class CityEntity extends TopologyFeatureEntity {
private int population;
public int getPopulation() {
return population;
}
public void setPopulation(int population) {
this.population = population;
}
}

View File

@ -0,0 +1,81 @@
/**
* 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.selection.qualifier.iterable;
import java.util.ArrayList;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
/**
*
* @author Sjaak Derksen
*/
@IssueKey( "707" )
@WithClasses( {
CityDto.class,
CityEntity.class,
RiverDto.class,
RiverEntity.class,
TopologyDto.class,
TopologyEntity.class,
TopologyFeatureDto.class,
TopologyFeatureEntity.class,
TopologyMapper.class
} )
@RunWith( AnnotationProcessorTestRunner.class )
public class IterableAndQualifiersTest {
@Test
public void testGenerationBasedOnQualifier() {
TopologyDto topologyDto1 = new TopologyDto();
List<TopologyFeatureDto> topologyFeatures1 = new ArrayList<TopologyFeatureDto>();
RiverDto riverDto = new RiverDto();
riverDto.setName( "Rhine" );
riverDto.setLength( 5 );
topologyFeatures1.add( riverDto );
topologyDto1.setTopologyFeatures( topologyFeatures1 );
TopologyEntity result1 = TopologyMapper.INSTANCE.mapTopologyAsRiver( topologyDto1 );
assertThat( result1.getTopologyFeatures() ).hasSize( 1 );
assertThat( result1.getTopologyFeatures().get( 0 ).getName() ).isEqualTo( "Rhine" );
assertThat( result1.getTopologyFeatures().get( 0 ) ).isInstanceOf( RiverEntity.class );
assertThat( ( (RiverEntity) result1.getTopologyFeatures().get( 0 ) ).getLength() ).isEqualTo( 5 );
TopologyDto topologyDto2 = new TopologyDto();
List<TopologyFeatureDto> topologyFeatures2 = new ArrayList<TopologyFeatureDto>();
CityDto cityDto = new CityDto();
cityDto.setName( "Amsterdam" );
cityDto.setPopulation( 800000 );
topologyFeatures2.add( cityDto );
topologyDto2.setTopologyFeatures( topologyFeatures2 );
TopologyEntity result2 = TopologyMapper.INSTANCE.mapTopologyAsCity( topologyDto2 );
assertThat( result2.getTopologyFeatures() ).hasSize( 1 );
assertThat( result2.getTopologyFeatures().get( 0 ).getName() ).isEqualTo( "Amsterdam" );
assertThat( result2.getTopologyFeatures().get( 0 ) ).isInstanceOf( CityEntity.class );
assertThat( ( (CityEntity) result2.getTopologyFeatures().get( 0 ) ).getPopulation() ).isEqualTo( 800000 );
}
}

View File

@ -0,0 +1,37 @@
/**
* 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.selection.qualifier.iterable;
/**
*
* @author Sjaak Derksen
*/
public class RiverDto extends TopologyFeatureDto {
private int length;
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
}

View File

@ -0,0 +1,37 @@
/**
* 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.selection.qualifier.iterable;
/**
*
* @author Sjaak Derksen
*/
public class RiverEntity extends TopologyFeatureEntity {
private int length;
public int getLength() {
return length;
}
public void setLength(int length) {
this.length = length;
}
}

View File

@ -0,0 +1,39 @@
/**
* 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.selection.qualifier.iterable;
import java.util.List;
/**
*
* @author Sjaak Derksen
*/
public class TopologyDto {
private List<TopologyFeatureDto> topologyFeatures;
public List<TopologyFeatureDto> getTopologyFeatures() {
return topologyFeatures;
}
public void setTopologyFeatures(List<TopologyFeatureDto> topologyFeatures) {
this.topologyFeatures = topologyFeatures;
}
}

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.selection.qualifier.iterable;
import java.util.List;
/**
*
* @author Sjaak Derksen
*/
public class TopologyEntity {
private List<TopologyFeatureEntity> topologyFeatures;
public List<TopologyFeatureEntity> getTopologyFeatures() {
return topologyFeatures;
}
public void setTopologyFeatures(List<TopologyFeatureEntity> topologyFeatures) {
this.topologyFeatures = topologyFeatures;
}
}

View File

@ -0,0 +1,37 @@
/**
* 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.selection.qualifier.iterable;
/**
*
* @author Sjaak Derksen
*/
public class TopologyFeatureDto {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,36 @@
/**
* 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.selection.qualifier.iterable;
/**
*
* @author Sjaak Derksen
*/
public class TopologyFeatureEntity {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,81 @@
/**
* 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.selection.qualifier.iterable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Qualifier;
import org.mapstruct.factory.Mappers;
/**
*
* @author Sjaak Derksen
*/
@Mapper
public abstract class TopologyMapper {
public static final TopologyMapper INSTANCE = Mappers.getMapper( TopologyMapper.class );
@Mapping( target = "topologyFeatures", qualifiedBy = Rivers.class )
public abstract TopologyEntity mapTopologyAsRiver(TopologyDto dto);
@Mapping( target = "topologyFeatures", qualifiedBy = Cities.class )
public abstract TopologyEntity mapTopologyAsCity(TopologyDto dto);
@Rivers
protected TopologyFeatureEntity mapRiver( TopologyFeatureDto dto ) {
TopologyFeatureEntity topologyFeatureEntity = null;
if ( dto instanceof RiverDto ) {
RiverEntity riverEntity = new RiverEntity();
riverEntity.setLength( ( (RiverDto) dto ).getLength() );
topologyFeatureEntity = riverEntity;
topologyFeatureEntity.setName( dto.getName() );
}
return topologyFeatureEntity;
}
@Cities
protected TopologyFeatureEntity mapCity( TopologyFeatureDto dto ) {
TopologyFeatureEntity topologyFeatureEntity = null;
if ( dto instanceof CityDto ) {
CityEntity cityEntity = new CityEntity();
cityEntity.setPopulation( ( (CityDto) dto ).getPopulation() );
topologyFeatureEntity = cityEntity;
topologyFeatureEntity.setName( dto.getName() );
}
return topologyFeatureEntity;
}
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Rivers {
}
@Qualifier
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)
public @interface Cities {
}
}