Skip to content

Commit 1c18516

Browse files
committed
mapstruct#72 Adding Mapping#ignore() to exclude specified properties from mapping
1 parent 6828218 commit 1c18516

9 files changed

Lines changed: 293 additions & 16 deletions

File tree

core-jdk8/src/main/java/org/mapstruct/Mapping.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,4 +93,14 @@
9393
* @return A constant {@code String} constant specifying the value for the designated target property
9494
*/
9595
String expression() default "";
96+
97+
/**
98+
* Whether the property specified via {@link #source()} or {@link #target()} should be ignored by the generated
99+
* mapping method or not. This can be useful when certain attributes should not be propagated from source or target
100+
* or when properties in the target object are populated using a decorator and thus would be reported as unmapped
101+
* target property by default.
102+
*
103+
* @return {@code true} if the given property should be ignored, {@code false} otherwise
104+
*/
105+
boolean ignore() default false;
96106
}

core/src/main/java/org/mapstruct/Mapping.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,13 @@
9292
*/
9393
String expression() default "";
9494

95+
/**
96+
* Whether the property specified via {@link #source()} or {@link #target()} should be ignored by the generated
97+
* mapping method or not. This can be useful when certain attributes should not be propagated from source or target
98+
* or when properties in the target object are populated using a decorator and thus would be reported as unmapped
99+
* target property by default.
100+
*
101+
* @return {@code true} if the given property should be ignored, {@code false} otherwise
102+
*/
103+
boolean ignore() default false;
95104
}

processor/src/main/java/org/mapstruct/ap/model/source/Mapping.java

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.util.Map;
2525
import java.util.regex.Matcher;
2626
import java.util.regex.Pattern;
27+
2728
import javax.annotation.processing.Messager;
2829
import javax.lang.model.element.AnnotationMirror;
2930
import javax.lang.model.element.AnnotationValue;
@@ -51,10 +52,12 @@ public class Mapping {
5152
private final String javaExpression;
5253
private final String targetName;
5354
private final String dateFormat;
55+
private final boolean isIgnored;
5456
private final AnnotationMirror mirror;
5557
private final AnnotationValue sourceAnnotationValue;
5658
private final AnnotationValue targetAnnotationValue;
5759

60+
5861
public static Map<String, List<Mapping>> fromMappingsPrism(MappingsPrism mappingsAnnotation, Element element,
5962
Messager messager) {
6063
Map<String, List<Mapping>> mappings = new HashMap<String, List<Mapping>>();
@@ -81,8 +84,9 @@ public static Mapping fromMappingPrism(MappingPrism mappingPrism, Element elemen
8184
);
8285

8386
if ( mappingPrism.source().isEmpty() &&
84-
mappingPrism.constant().isEmpty() &&
85-
mappingPrism.expression().isEmpty() ) {
87+
mappingPrism.constant().isEmpty() &&
88+
mappingPrism.expression().isEmpty() &&
89+
!mappingPrism.ignore() ) {
8690
messager.printMessage(
8791
Diagnostic.Kind.ERROR,
8892
"Either define a source, a constant or an expression in a Mapping",
@@ -122,6 +126,7 @@ else if ( !mappingPrism.expression().isEmpty() && !mappingPrism.constant().isEmp
122126
mappingPrism.expression(),
123127
mappingPrism.target(),
124128
mappingPrism.dateFormat(),
129+
mappingPrism.ignore(),
125130
mappingPrism.mirror,
126131
mappingPrism.values.source(),
127132
mappingPrism.values.target()
@@ -147,8 +152,9 @@ private static String[] getSourceNameParts(String sourceName, Element element, A
147152
return parts;
148153
}
149154

155+
//CHECKSTYLE:OFF
150156
private Mapping(String sourceName, String sourceParameterName, String sourcePropertyName, String constant,
151-
String expression, String targetName, String dateFormat, AnnotationMirror mirror,
157+
String expression, String targetName, String dateFormat, boolean isIgnored, AnnotationMirror mirror,
152158
AnnotationValue sourceAnnotationValue, AnnotationValue targetAnnotationValue) {
153159
this.sourceName = sourceName;
154160
this.sourceParameterName = sourceParameterName;
@@ -159,10 +165,12 @@ private Mapping(String sourceName, String sourceParameterName, String sourceProp
159165
this.javaExpression = javaExpressionMatcher.matches() ? javaExpressionMatcher.group( 1 ).trim() : "";
160166
this.targetName = targetName.equals( "" ) ? sourceName : targetName;
161167
this.dateFormat = dateFormat;
168+
this.isIgnored = isIgnored;
162169
this.mirror = mirror;
163170
this.sourceAnnotationValue = sourceAnnotationValue;
164171
this.targetAnnotationValue = targetAnnotationValue;
165172
}
173+
//CHECKSTYLE:ON
166174

167175
/**
168176
* Returns the complete source name of this mapping, either a qualified (e.g. {@code parameter1.foo}) or
@@ -208,6 +216,10 @@ public String getDateFormat() {
208216
return dateFormat;
209217
}
210218

219+
public boolean isIgnored() {
220+
return isIgnored;
221+
}
222+
211223
public AnnotationMirror getMirror() {
212224
return mirror;
213225
}
@@ -220,11 +232,10 @@ public AnnotationValue getTargetAnnotationValue() {
220232
return targetAnnotationValue;
221233
}
222234

223-
224235
public Mapping reverse() {
225236
Mapping reverse = null;
226-
if ( constant != null ) {
227-
/* mapping can only be reversed if the source was not a constant */
237+
// mapping can only be reversed if the source was not a constant nor an expression
238+
if ( constant != null && expression != null ) {
228239
reverse = new Mapping(
229240
targetName,
230241
null,
@@ -233,6 +244,7 @@ public Mapping reverse() {
233244
expression,
234245
sourceName,
235246
dateFormat,
247+
isIgnored,
236248
mirror,
237249
sourceAnnotationValue,
238250
targetAnnotationValue

processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -483,9 +483,9 @@ private BeanMappingMethod getBeanMappingMethod(List<MapperReference> mapperRefer
483483
ReportingPolicy unmappedTargetPolicy = getEffectiveUnmappedTargetPolicy( element );
484484
CollectionMappingStrategy cmStrategy = getEffectiveCollectionMappingStrategy( element );
485485

486-
487486
List<PropertyMapping> propertyMappings = new ArrayList<PropertyMapping>();
488487
Set<String> mappedTargetProperties = new HashSet<String>();
488+
Set<String> ignoredTargetProperties = new HashSet<String>();
489489

490490
if ( !reportErrorIfMappedPropertiesDontExist( method ) ) {
491491
return null;
@@ -501,6 +501,11 @@ private BeanMappingMethod getBeanMappingMethod(List<MapperReference> mapperRefer
501501

502502
Mapping mapping = method.getMappingByTargetPropertyName( targetPropertyName );
503503

504+
if ( mapping != null && mapping.isIgnored() ) {
505+
ignoredTargetProperties.add( targetPropertyName );
506+
continue;
507+
}
508+
504509
// A target access is in general a setter method on the target object. However, in case of collections,
505510
// the current target accessor can also be a getter method.
506511
//
@@ -578,7 +583,8 @@ else if ( newPropertyMapping != null ) {
578583
method,
579584
unmappedTargetPolicy,
580585
targetProperties,
581-
mappedTargetProperties
586+
mappedTargetProperties,
587+
ignoredTargetProperties
582588
);
583589

584590
FactoryMethod factoryMethod = getFactoryMethod( mapperReferences, methods, method.getReturnType() );
@@ -588,17 +594,24 @@ else if ( newPropertyMapping != null ) {
588594
private void reportErrorForUnmappedTargetPropertiesIfRequired(SourceMethod method,
589595
ReportingPolicy unmappedTargetPolicy,
590596
Set<String> targetProperties,
591-
Set<String> mappedTargetProperties) {
597+
Set<String> mappedTargetProperties,
598+
Set<String> ignoredTargetProperties) {
599+
600+
Set<String> unmappedTargetProperties = new HashSet<String>();
592601

593-
if ( targetProperties.size() > mappedTargetProperties.size() &&
594-
unmappedTargetPolicy.requiresReport() ) {
595-
targetProperties.removeAll( mappedTargetProperties );
602+
for ( String property : targetProperties ) {
603+
if ( !mappedTargetProperties.contains( property ) && !ignoredTargetProperties.contains( property ) ) {
604+
unmappedTargetProperties.add( property );
605+
}
606+
}
607+
608+
if ( !unmappedTargetProperties.isEmpty() && unmappedTargetPolicy.requiresReport() ) {
596609
messager.printMessage(
597610
unmappedTargetPolicy.getDiagnosticKind(),
598611
MessageFormat.format(
599612
"Unmapped target {0,choice,1#property|1<properties}: \"{1}\"",
600-
targetProperties.size(),
601-
Strings.join( targetProperties, ", " )
613+
unmappedTargetProperties.size(),
614+
Strings.join( unmappedTargetProperties, ", " )
602615
),
603616
method.getExecutable()
604617
);
@@ -646,6 +659,10 @@ private boolean reportErrorIfMappedPropertiesDontExist(SourceMethod method) {
646659

647660
for ( List<Mapping> mappedProperties : method.getMappings().values() ) {
648661
for ( Mapping mappedProperty : mappedProperties ) {
662+
if ( mappedProperty.isIgnored() ) {
663+
continue;
664+
}
665+
649666
if ( mappedProperty.getSourceParameterName() != null ) {
650667
Parameter sourceParameter = method.getSourceParameter( mappedProperty.getSourceParameterName() );
651668

processor/src/test/java/org/mapstruct/ap/test/decorator/PersonMapper.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@
2020

2121
import org.mapstruct.DecoratedWith;
2222
import org.mapstruct.Mapper;
23-
import org.mapstruct.ReportingPolicy;
23+
import org.mapstruct.Mapping;
2424
import org.mapstruct.factory.Mappers;
2525

26-
@Mapper(unmappedTargetPolicy = ReportingPolicy.IGNORE)
26+
@Mapper
2727
@DecoratedWith(PersonMapperDecorator.class)
2828
public interface PersonMapper {
2929

3030
PersonMapper INSTANCE = Mappers.getMapper( PersonMapper.class );
3131

32+
@Mapping( target = "name", ignore = true )
3233
PersonDto personToPersonDto(Person person);
3334

3435
AddressDto addressToAddressDto(Address address);
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
3+
* and/or other contributors as indicated by the @authors tag. See the
4+
* copyright.txt file in the distribution for a full listing of all
5+
* contributors.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.mapstruct.ap.test.ignore;
20+
21+
public class Animal {
22+
23+
private String name;
24+
private int size;
25+
private Integer age;
26+
27+
public Animal() {
28+
}
29+
30+
public Animal(String name, int size, Integer age) {
31+
this.name = name;
32+
this.size = size;
33+
this.age = age;
34+
}
35+
36+
public String getName() {
37+
return name;
38+
}
39+
40+
public void setName(String name) {
41+
this.name = name;
42+
}
43+
44+
public int getSize() {
45+
return size;
46+
}
47+
48+
public void setSize(int size) {
49+
this.size = size;
50+
}
51+
52+
public Integer getAge() {
53+
return age;
54+
}
55+
56+
public void setAge(Integer age) {
57+
this.age = age;
58+
}
59+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
3+
* and/or other contributors as indicated by the @authors tag. See the
4+
* copyright.txt file in the distribution for a full listing of all
5+
* contributors.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.mapstruct.ap.test.ignore;
20+
21+
public class AnimalDto {
22+
23+
private String name;
24+
private Integer size;
25+
private Integer age;
26+
27+
public AnimalDto() {
28+
29+
}
30+
31+
public AnimalDto(String name, Integer size, Integer age) {
32+
this.name = name;
33+
this.size = size;
34+
this.age = age;
35+
}
36+
37+
public String getName() {
38+
return name;
39+
}
40+
41+
public void setName(String name) {
42+
this.name = name;
43+
}
44+
45+
public Integer getSize() {
46+
return size;
47+
}
48+
49+
public void setSize(Integer size) {
50+
this.size = size;
51+
}
52+
53+
public Integer getAge() {
54+
return age;
55+
}
56+
57+
public void setAge(Integer age) {
58+
this.age = age;
59+
}
60+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Copyright 2012-2014 Gunnar Morling (http://www.gunnarmorling.de/)
3+
* and/or other contributors as indicated by the @authors tag. See the
4+
* copyright.txt file in the distribution for a full listing of all
5+
* contributors.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
*/
19+
package org.mapstruct.ap.test.ignore;
20+
21+
import org.mapstruct.Mapper;
22+
import org.mapstruct.Mapping;
23+
import org.mapstruct.Mappings;
24+
import org.mapstruct.factory.Mappers;
25+
26+
@Mapper
27+
public interface AnimalMapper {
28+
29+
AnimalMapper INSTANCE = Mappers.getMapper( AnimalMapper.class );
30+
31+
@Mappings({
32+
@Mapping(source = "size", ignore = true),
33+
@Mapping(target = "age", ignore = true)
34+
})
35+
AnimalDto animalToDto(Animal animal);
36+
37+
Animal animalDtoToAnimal(AnimalDto animalDto);
38+
}

0 commit comments

Comments
 (0)