Skip to content

Commit dc7bf4b

Browse files
committed
add ignored annotation with prefix and repeatable for issues #1958
1 parent 4812d2b commit dc7bf4b

14 files changed

Lines changed: 634 additions & 1 deletion

File tree

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct;
7+
8+
import java.lang.annotation.ElementType;
9+
import java.lang.annotation.Repeatable;
10+
import java.lang.annotation.Retention;
11+
import java.lang.annotation.RetentionPolicy;
12+
import java.lang.annotation.Target;
13+
14+
/**
15+
* Configures the ignored of one bean attribute.
16+
*
17+
* <p>
18+
* The name all attributes of for ignored is to be specified via {@link #targets()}.
19+
* </p>
20+
*
21+
* <p>
22+
* <strong>Example 1:</strong> Implicitly mapping fields with the same name:
23+
* </p>
24+
*
25+
* <pre><code class='java'>
26+
* // We need ignored Human.name and Human.lastName
27+
* // we can use &#64;Ignored with parameters "name", "lastName" {@link #targets()}
28+
* &#64;Mapper
29+
* public interface HumanMapper {
30+
* &#64;Ignored( { "name", "lastName" } )
31+
* HumanDto toHumanDto(Human human)
32+
* }
33+
* </code></pre>
34+
* <pre><code class='java'>
35+
* // generates:
36+
* &#64;Override
37+
* public HumanDto toHumanDto(Human human) {
38+
* humanDto.setFullName( human.getFullName() );
39+
* // ...
40+
* }
41+
* </code></pre>
42+
*
43+
* @author Ivashin Aleksey
44+
* @see Javadoc
45+
*/
46+
@Repeatable(Ignoreds.class)
47+
@Retention(RetentionPolicy.CLASS)
48+
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
49+
public @interface Ignored {
50+
51+
/**
52+
* Whether the properties specified via {@link #targets()} should be ignored by the generated mapping method.
53+
* This can be useful when certain attributes should not be propagated from source to target or when properties in
54+
* the target object are populated using a decorator and thus would be reported as unmapped target property by
55+
* default.
56+
*
57+
* @return The target names of the configured property for ignored
58+
*/
59+
String[] targets();
60+
61+
/**
62+
* Whether the prefix specified via {@link #prefix()} should be ignored by the generated mapping method.
63+
* This can be useful when certain attributes should not be propagated from source to target or when properties in
64+
* the target object are populated using a decorator and thus would be reported as unmapped target property by
65+
* default.
66+
*
67+
* @return The target prefix of the configured properties for ignored
68+
*/
69+
String prefix() default "";
70+
71+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct;
7+
8+
import java.lang.annotation.ElementType;
9+
import java.lang.annotation.Retention;
10+
import java.lang.annotation.RetentionPolicy;
11+
import java.lang.annotation.Target;
12+
13+
/**
14+
* Configures the ignoreds of several bean attributes.
15+
* <p>
16+
* <strong>TIP: When using Java 8 or later, you can omit the @Ingoreds
17+
* wrapper annotation and directly specify several @Ingored annotations on one method.</strong>
18+
*
19+
* <p>These two examples are equal.
20+
* </p>
21+
* <pre><code class='java'>
22+
* // before Java 8
23+
* &#64;Mapper
24+
* public interface MyMapper {
25+
* &#64;Ingoreds({
26+
* &#64;Ingored(targets = { "firstProperty" } ),
27+
* &#64;Ingored(targets = { "secondProperty" } )
28+
* })
29+
* HumanDto toHumanDto(Human human);
30+
* }
31+
* </code></pre>
32+
* <pre><code class='java'>
33+
* // Java 8 and later
34+
* &#64;Mapper
35+
* public interface MyMapper {
36+
* &#64;Ingored(targets = { "firstProperty" } ),
37+
* &#64;Ingored(targets = { "secondProperty" } )
38+
* HumanDto toHumanDto(Human human);
39+
* }
40+
* </code></pre>
41+
*
42+
* @author Ivashin Aleksey
43+
*/
44+
@Retention(RetentionPolicy.CLASS)
45+
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE })
46+
public @interface Ignoreds {
47+
48+
/**
49+
* The configuration of the bean attributes.
50+
*
51+
* @return The configuration of the bean attributes.
52+
*/
53+
Ignored[] value();
54+
}

processor/src/main/java/org/mapstruct/ap/internal/gem/GemGenerator.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
import org.mapstruct.Mapper;
2727
import org.mapstruct.MapperConfig;
2828
import org.mapstruct.Mapping;
29+
import org.mapstruct.Ignored;
30+
import org.mapstruct.Ignoreds;
2931
import org.mapstruct.MappingTarget;
3032
import org.mapstruct.Mappings;
3133
import org.mapstruct.Named;
@@ -53,6 +55,8 @@
5355
@GemDefinition(AnnotateWiths.class)
5456
@GemDefinition(Mapper.class)
5557
@GemDefinition(Mapping.class)
58+
@GemDefinition(Ignored.class)
59+
@GemDefinition(Ignoreds.class)
5660
@GemDefinition(Mappings.class)
5761
@GemDefinition(IterableMapping.class)
5862
@GemDefinition(BeanMapping.class)

processor/src/main/java/org/mapstruct/ap/internal/processor/MethodRetrievalProcessor.java

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@
2121

2222
import org.mapstruct.ap.internal.gem.BeanMappingGem;
2323
import org.mapstruct.ap.internal.gem.ConditionGem;
24+
import org.mapstruct.ap.internal.gem.IgnoredGem;
25+
import org.mapstruct.ap.internal.gem.IgnoredsGem;
2426
import org.mapstruct.ap.internal.gem.IterableMappingGem;
2527
import org.mapstruct.ap.internal.gem.MapMappingGem;
2628
import org.mapstruct.ap.internal.gem.MappingGem;
@@ -76,6 +78,8 @@ public class MethodRetrievalProcessor implements ModelElementProcessor<Void, Lis
7678
private static final String VALUE_MAPPING_FQN = "org.mapstruct.ValueMapping";
7779
private static final String VALUE_MAPPINGS_FQN = "org.mapstruct.ValueMappings";
7880
private static final String CONDITION_FQN = "org.mapstruct.Condition";
81+
private static final String IGNORED_FQN = "org.mapstruct.Ignored";
82+
private static final String IGNOREDS_FQN = "org.mapstruct.Ignoreds";
7983
private FormattingMessager messager;
8084
private TypeFactory typeFactory;
8185
private AccessorNamingUtils accessorNaming;
@@ -619,7 +623,10 @@ private boolean isStreamTypeOrIterableFromJavaStdLib(Type type) {
619623
* @return The mappings for the given method, keyed by target property name
620624
*/
621625
private Set<MappingOptions> getMappings(ExecutableElement method, BeanMappingOptions beanMapping) {
622-
return new RepeatableMappings( beanMapping ).getProcessedAnnotations( method );
626+
Set<MappingOptions> processedAnnotations = new RepeatableMappings( beanMapping )
627+
.getProcessedAnnotations( method );
628+
processedAnnotations.addAll( new IgnoredConditions().getProcessedAnnotations( method ) );
629+
return processedAnnotations;
623630
}
624631

625632
/**
@@ -818,4 +825,50 @@ protected void addInstance(ConditionGem gem, Element source, Set<ConditionOption
818825
}
819826
}
820827
}
828+
829+
private class IgnoredConditions extends RepeatableAnnotations<IgnoredGem, IgnoredsGem, MappingOptions> {
830+
831+
protected IgnoredConditions() {
832+
super( elementUtils, IGNORED_FQN, IGNOREDS_FQN );
833+
}
834+
835+
@Override
836+
protected IgnoredGem singularInstanceOn(Element element) {
837+
return IgnoredGem.instanceOn( element );
838+
}
839+
840+
@Override
841+
protected IgnoredsGem multipleInstanceOn(Element element) {
842+
return IgnoredsGem.instanceOn( element );
843+
}
844+
845+
@Override
846+
protected void addInstance(IgnoredGem gem, Element method, Set<MappingOptions> mappings) {
847+
IgnoredGem ignoredGem = IgnoredGem.instanceOn( method );
848+
if ( ignoredGem == null ) {
849+
ignoredGem = gem;
850+
}
851+
String prefix = ignoredGem.prefix().get();
852+
for ( String target : ignoredGem.targets().get() ) {
853+
String realTarget = target;
854+
if ( !prefix.isEmpty() ) {
855+
realTarget = prefix + "." + target;
856+
}
857+
MappingOptions mappingOptions = MappingOptions.forIgnore( realTarget );
858+
if ( mappings.contains( mappingOptions ) ) {
859+
messager.printMessage( method, Message.PROPERTYMAPPING_DUPLICATE_TARGETS, realTarget );
860+
}
861+
mappings.add( mappingOptions );
862+
}
863+
}
864+
865+
@Override
866+
protected void addInstances(IgnoredsGem gem, Element method, Set<MappingOptions> mappings) {
867+
IgnoredsGem ignoredsGem = IgnoredsGem.instanceOn( method );
868+
for ( IgnoredGem ignoredGem : ignoredsGem.value().get() ) {
869+
addInstance( ignoredGem, method, mappings );
870+
}
871+
}
872+
}
873+
821874
}
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.ignored;
7+
8+
public class Animal {
9+
10+
//CHECKSTYLE:OFF
11+
public Integer publicAge;
12+
public String publicColour;
13+
//CHECKSTYLE:OFN
14+
private String colour;
15+
private String name;
16+
private int size;
17+
private Integer age;
18+
19+
// private String colour;
20+
public Animal() {
21+
}
22+
23+
public Animal(String name, int size, Integer age, String colour) {
24+
this.name = name;
25+
this.size = size;
26+
this.publicAge = age;
27+
this.age = age;
28+
this.publicColour = colour;
29+
this.colour = colour;
30+
}
31+
32+
public String getName() {
33+
return name;
34+
}
35+
36+
public void setName(String name) {
37+
this.name = name;
38+
}
39+
40+
public int getSize() {
41+
return size;
42+
}
43+
44+
public void setSize(int size) {
45+
this.size = size;
46+
}
47+
48+
public Integer getAge() {
49+
return age;
50+
}
51+
52+
public void setAge(Integer age) {
53+
this.age = age;
54+
}
55+
56+
public String getColour() {
57+
return colour;
58+
}
59+
60+
public void setColour( String colour ) {
61+
this.colour = colour;
62+
}
63+
64+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.ignored;
7+
8+
public class AnimalDto {
9+
10+
//CHECKSTYLE:OFF
11+
public Integer publicAge;
12+
public String publicColor;
13+
//CHECKSTYLE:ON
14+
private String name;
15+
private Integer size;
16+
private Integer age;
17+
private String color;
18+
19+
public AnimalDto() {
20+
21+
}
22+
23+
public AnimalDto(String name, Integer size, Integer age, String color) {
24+
this.name = name;
25+
this.size = size;
26+
this.publicAge = age;
27+
this.age = age;
28+
this.publicColor = color;
29+
this.color = color;
30+
}
31+
32+
public String getName() {
33+
return name;
34+
}
35+
36+
public void setName(String name) {
37+
this.name = name;
38+
}
39+
40+
public Integer getSize() {
41+
return size;
42+
}
43+
44+
public void setSize(Integer size) {
45+
this.size = size;
46+
}
47+
48+
public Integer getAge() {
49+
return age;
50+
}
51+
52+
public void setAge(Integer age) {
53+
this.age = age;
54+
}
55+
56+
public String getColor() {
57+
return color;
58+
}
59+
60+
public void setColor(String color) {
61+
this.color = color;
62+
}
63+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.ignored;
7+
8+
import org.mapstruct.BeanMapping;
9+
import org.mapstruct.Ignored;
10+
import org.mapstruct.Mapper;
11+
import org.mapstruct.factory.Mappers;
12+
13+
@Mapper
14+
public interface AnimalMapper {
15+
16+
AnimalMapper INSTANCE = Mappers.getMapper( AnimalMapper.class );
17+
18+
@Ignored( targets = { "publicAge", "age", "publicColor", "color" } )
19+
AnimalDto animalToDto( Animal animal );
20+
21+
@BeanMapping( ignoreByDefault = true )
22+
AnimalDto animalToDtoIgnoreAll( Animal animal );
23+
24+
}

0 commit comments

Comments
 (0)