mirror of
https://github.com/mapstruct/mapstruct.git
synced 2025-07-12 00:00:08 +08:00
#72 Adding Mapping#ignore() to exclude specified properties from mapping
This commit is contained in:
parent
68282180b9
commit
1c18516793
@ -93,4 +93,14 @@ public @interface Mapping {
|
||||
* @return A constant {@code String} constant specifying the value for the designated target property
|
||||
*/
|
||||
String expression() default "";
|
||||
|
||||
/**
|
||||
* Whether the property specified via {@link #source()} or {@link #target()} should be ignored by the generated
|
||||
* mapping method or not. This can be useful when certain attributes should not be propagated from source or target
|
||||
* or when properties in the target object are populated using a decorator and thus would be reported as unmapped
|
||||
* target property by default.
|
||||
*
|
||||
* @return {@code true} if the given property should be ignored, {@code false} otherwise
|
||||
*/
|
||||
boolean ignore() default false;
|
||||
}
|
||||
|
@ -92,4 +92,13 @@ public @interface Mapping {
|
||||
*/
|
||||
String expression() default "";
|
||||
|
||||
/**
|
||||
* Whether the property specified via {@link #source()} or {@link #target()} should be ignored by the generated
|
||||
* mapping method or not. This can be useful when certain attributes should not be propagated from source or target
|
||||
* or when properties in the target object are populated using a decorator and thus would be reported as unmapped
|
||||
* target property by default.
|
||||
*
|
||||
* @return {@code true} if the given property should be ignored, {@code false} otherwise
|
||||
*/
|
||||
boolean ignore() default false;
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.annotation.processing.Messager;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.AnnotationValue;
|
||||
@ -51,10 +52,12 @@ public class Mapping {
|
||||
private final String javaExpression;
|
||||
private final String targetName;
|
||||
private final String dateFormat;
|
||||
private final boolean isIgnored;
|
||||
private final AnnotationMirror mirror;
|
||||
private final AnnotationValue sourceAnnotationValue;
|
||||
private final AnnotationValue targetAnnotationValue;
|
||||
|
||||
|
||||
public static Map<String, List<Mapping>> fromMappingsPrism(MappingsPrism mappingsAnnotation, Element element,
|
||||
Messager messager) {
|
||||
Map<String, List<Mapping>> mappings = new HashMap<String, List<Mapping>>();
|
||||
@ -81,8 +84,9 @@ public class Mapping {
|
||||
);
|
||||
|
||||
if ( mappingPrism.source().isEmpty() &&
|
||||
mappingPrism.constant().isEmpty() &&
|
||||
mappingPrism.expression().isEmpty() ) {
|
||||
mappingPrism.constant().isEmpty() &&
|
||||
mappingPrism.expression().isEmpty() &&
|
||||
!mappingPrism.ignore() ) {
|
||||
messager.printMessage(
|
||||
Diagnostic.Kind.ERROR,
|
||||
"Either define a source, a constant or an expression in a Mapping",
|
||||
@ -122,6 +126,7 @@ public class Mapping {
|
||||
mappingPrism.expression(),
|
||||
mappingPrism.target(),
|
||||
mappingPrism.dateFormat(),
|
||||
mappingPrism.ignore(),
|
||||
mappingPrism.mirror,
|
||||
mappingPrism.values.source(),
|
||||
mappingPrism.values.target()
|
||||
@ -147,8 +152,9 @@ public class Mapping {
|
||||
return parts;
|
||||
}
|
||||
|
||||
//CHECKSTYLE:OFF
|
||||
private Mapping(String sourceName, String sourceParameterName, String sourcePropertyName, String constant,
|
||||
String expression, String targetName, String dateFormat, AnnotationMirror mirror,
|
||||
String expression, String targetName, String dateFormat, boolean isIgnored, AnnotationMirror mirror,
|
||||
AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue) {
|
||||
this.sourceName = sourceName;
|
||||
this.sourceParameterName = sourceParameterName;
|
||||
@ -159,10 +165,12 @@ public class Mapping {
|
||||
this.javaExpression = javaExpressionMatcher.matches() ? javaExpressionMatcher.group( 1 ).trim() : "";
|
||||
this.targetName = targetName.equals( "" ) ? sourceName : targetName;
|
||||
this.dateFormat = dateFormat;
|
||||
this.isIgnored = isIgnored;
|
||||
this.mirror = mirror;
|
||||
this.sourceAnnotationValue = sourceAnnotationValue;
|
||||
this.targetAnnotationValue = targetAnnotationValue;
|
||||
}
|
||||
//CHECKSTYLE:ON
|
||||
|
||||
/**
|
||||
* Returns the complete source name of this mapping, either a qualified (e.g. {@code parameter1.foo}) or
|
||||
@ -208,6 +216,10 @@ public class Mapping {
|
||||
return dateFormat;
|
||||
}
|
||||
|
||||
public boolean isIgnored() {
|
||||
return isIgnored;
|
||||
}
|
||||
|
||||
public AnnotationMirror getMirror() {
|
||||
return mirror;
|
||||
}
|
||||
@ -220,11 +232,10 @@ public class Mapping {
|
||||
return targetAnnotationValue;
|
||||
}
|
||||
|
||||
|
||||
public Mapping reverse() {
|
||||
Mapping reverse = null;
|
||||
if ( constant != null ) {
|
||||
/* mapping can only be reversed if the source was not a constant */
|
||||
// mapping can only be reversed if the source was not a constant nor an expression
|
||||
if ( constant != null && expression != null ) {
|
||||
reverse = new Mapping(
|
||||
targetName,
|
||||
null,
|
||||
@ -233,6 +244,7 @@ public class Mapping {
|
||||
expression,
|
||||
sourceName,
|
||||
dateFormat,
|
||||
isIgnored,
|
||||
mirror,
|
||||
sourceAnnotationValue,
|
||||
targetAnnotationValue
|
||||
|
@ -483,9 +483,9 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
ReportingPolicy unmappedTargetPolicy = getEffectiveUnmappedTargetPolicy( element );
|
||||
CollectionMappingStrategy cmStrategy = getEffectiveCollectionMappingStrategy( element );
|
||||
|
||||
|
||||
List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>();
|
||||
Set<String> mappedTargetProperties = new HashSet<String>();
|
||||
Set<String> ignoredTargetProperties = new HashSet<String>();
|
||||
|
||||
if ( !reportErrorIfMappedPropertiesDontExist( method ) ) {
|
||||
return null;
|
||||
@ -501,6 +501,11 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
|
||||
Mapping mapping = method.getMappingByTargetPropertyName( targetPropertyName );
|
||||
|
||||
if ( mapping != null && mapping.isIgnored() ) {
|
||||
ignoredTargetProperties.add( targetPropertyName );
|
||||
continue;
|
||||
}
|
||||
|
||||
// A target access is in general a setter method on the target object. However, in case of collections,
|
||||
// the current target accessor can also be a getter method.
|
||||
//
|
||||
@ -578,7 +583,8 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
method,
|
||||
unmappedTargetPolicy,
|
||||
targetProperties,
|
||||
mappedTargetProperties
|
||||
mappedTargetProperties,
|
||||
ignoredTargetProperties
|
||||
);
|
||||
|
||||
FactoryMethod factoryMethod = getFactoryMethod( mapperReferences, methods, method.getReturnType() );
|
||||
@ -588,17 +594,24 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
private void reportErrorForUnmappedTargetPropertiesIfRequired(SourceMethod method,
|
||||
ReportingPolicy unmappedTargetPolicy,
|
||||
Set<String> targetProperties,
|
||||
Set<String> mappedTargetProperties) {
|
||||
Set<String> mappedTargetProperties,
|
||||
Set<String> ignoredTargetProperties) {
|
||||
|
||||
if ( targetProperties.size() > mappedTargetProperties.size() &&
|
||||
unmappedTargetPolicy.requiresReport() ) {
|
||||
targetProperties.removeAll( mappedTargetProperties );
|
||||
Set<String> unmappedTargetProperties = new HashSet<String>();
|
||||
|
||||
for ( String property : targetProperties ) {
|
||||
if ( !mappedTargetProperties.contains( property ) && !ignoredTargetProperties.contains( property ) ) {
|
||||
unmappedTargetProperties.add( property );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !unmappedTargetProperties.isEmpty() && unmappedTargetPolicy.requiresReport() ) {
|
||||
messager.printMessage(
|
||||
unmappedTargetPolicy.getDiagnosticKind(),
|
||||
MessageFormat.format(
|
||||
"Unmapped target {0,choice,1#property|1<properties}: \"{1}\"",
|
||||
targetProperties.size(),
|
||||
Strings.join( targetProperties, ", " )
|
||||
unmappedTargetProperties.size(),
|
||||
Strings.join( unmappedTargetProperties, ", " )
|
||||
),
|
||||
method.getExecutable()
|
||||
);
|
||||
@ -646,6 +659,10 @@ public class MapperCreationProcessor implements ModelElementProcessor<List<Sourc
|
||||
|
||||
for ( List<Mapping> mappedProperties : method.getMappings().values() ) {
|
||||
for ( Mapping mappedProperty : mappedProperties ) {
|
||||
if ( mappedProperty.isIgnored() ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( mappedProperty.getSourceParameterName() != null ) {
|
||||
Parameter sourceParameter = method.getSourceParameter( mappedProperty.getSourceParameterName() );
|
||||
|
||||
|
@ -20,15 +20,16 @@ package org.mapstruct.ap.test.decorator;
|
||||
|
||||
import org.mapstruct.DecoratedWith;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.ReportingPolicy;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
|
||||
@Mapper
|
||||
@DecoratedWith(PersonMapperDecorator.class)
|
||||
public interface PersonMapper {
|
||||
|
||||
PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class );
|
||||
|
||||
@Mapping( target = "name", ignore = true )
|
||||
PersonDto personToPersonDto(Person person);
|
||||
|
||||
AddressDto addressToAddressDto(Address address);
|
||||
|
@ -0,0 +1,59 @@
|
||||
/**
|
||||
* 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.ignore;
|
||||
|
||||
public class Animal {
|
||||
|
||||
private String name;
|
||||
private int size;
|
||||
private Integer age;
|
||||
|
||||
public Animal() {
|
||||
}
|
||||
|
||||
public Animal(String name, int size, Integer age) {
|
||||
this.name = name;
|
||||
this.size = size;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(int size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public Integer getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(Integer age) {
|
||||
this.age = age;
|
||||
}
|
||||
}
|
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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.ignore;
|
||||
|
||||
public class AnimalDto {
|
||||
|
||||
private String name;
|
||||
private Integer size;
|
||||
private Integer age;
|
||||
|
||||
public AnimalDto() {
|
||||
|
||||
}
|
||||
|
||||
public AnimalDto(String name, Integer size, Integer age) {
|
||||
this.name = name;
|
||||
this.size = size;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Integer getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(Integer size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public Integer getAge() {
|
||||
return age;
|
||||
}
|
||||
|
||||
public void setAge(Integer age) {
|
||||
this.age = age;
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/**
|
||||
* 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.ignore;
|
||||
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
|
||||
@Mapper
|
||||
public interface AnimalMapper {
|
||||
|
||||
AnimalMapper INSTANCE = Mappers.getMapper( AnimalMapper.class );
|
||||
|
||||
@Mappings({
|
||||
@Mapping(source = "size", ignore = true),
|
||||
@Mapping(target = "age", ignore = true)
|
||||
})
|
||||
AnimalDto animalToDto(Animal animal);
|
||||
|
||||
Animal animalDtoToAnimal(AnimalDto animalDto);
|
||||
}
|
@ -0,0 +1,71 @@
|
||||
/**
|
||||
* 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.ignore;
|
||||
|
||||
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;
|
||||
|
||||
import static org.fest.assertions.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Test for ignoring properties during the mapping.
|
||||
*
|
||||
* @author Gunnar Morling
|
||||
*/
|
||||
@WithClasses({ Animal.class, AnimalDto.class, AnimalMapper.class })
|
||||
@RunWith(AnnotationProcessorTestRunner.class)
|
||||
public class IgnorePropertyTest {
|
||||
|
||||
@Test
|
||||
@IssueKey("72")
|
||||
public void shouldNotPropagateIgnoredPropertyGivenViaSourceAttribute() {
|
||||
Animal animal = new Animal( "Bruno", 100, 23 );
|
||||
|
||||
AnimalDto animalDto = AnimalMapper.INSTANCE.animalToDto( animal );
|
||||
|
||||
assertThat( animalDto ).isNotNull();
|
||||
assertThat( animalDto.getName() ).isEqualTo( "Bruno" );
|
||||
assertThat( animalDto.getSize() ).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@IssueKey("72")
|
||||
public void shouldNotPropagateIgnoredPropertyGivenViaTargetAttribute() {
|
||||
Animal animal = new Animal( "Bruno", 100, 23 );
|
||||
|
||||
AnimalDto animalDto = AnimalMapper.INSTANCE.animalToDto( animal );
|
||||
|
||||
assertThat( animalDto ).isNotNull();
|
||||
assertThat( animalDto.getAge() ).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@IssueKey("72")
|
||||
public void shouldNotPropagateIgnoredPropertyInReverseMapping() {
|
||||
AnimalDto animalDto = new AnimalDto( "Bruno", 100, 23 );
|
||||
|
||||
Animal animal = AnimalMapper.INSTANCE.animalDtoToAnimal( animalDto );
|
||||
|
||||
assertThat( animal ).isNotNull();
|
||||
assertThat( animal.getAge() ).isNull();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user