Skip to content

Commit 35f1712

Browse files
committed
Use target properties from meta annotations when looking for unmapped target properties
Fixes #49
1 parent 5b87efb commit 35f1712

File tree

7 files changed

+192
-15
lines changed

7 files changed

+192
-15
lines changed

change-notes.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
<h2>1.2.1</h2>
33
<ul>
44
<li>Support code completion in Mapping#expression and Mapping#defaultExpression</li>
5+
<li>Support meta annotations for when looking for unmapped target properties</li>
56
<li>Bug fix: public static fields / methods should not be considered as unmapped properties or resolved in auto completion</li>
67
</ul>
78
<h2>1.2.0</h2>

src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ public void visitMethod(PsiMethod method) {
7777
Set<String> allTargetProperties = findAllTargetProperties( targetType, mapStructVersion );
7878

7979
// find and remove all defined mapping targets
80-
Set<String> definedTargets = TargetUtils.findAllDefinedMappingTargets( method )
80+
Set<String> definedTargets = TargetUtils.findAllDefinedMappingTargets( method, mapStructVersion )
8181
.collect( Collectors.toSet() );
8282
allTargetProperties.removeAll( definedTargets );
8383

src/main/java/org/mapstruct/intellij/util/TargetUtils.java

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@
1818
import java.util.Set;
1919
import java.util.stream.Stream;
2020

21+
import com.intellij.codeInsight.AnnotationUtil;
22+
import com.intellij.codeInsight.MetaAnnotationUtil;
2123
import com.intellij.lang.jvm.JvmModifier;
2224
import com.intellij.openapi.util.Pair;
23-
import com.intellij.psi.ElementManipulators;
2425
import com.intellij.psi.PsiAnnotation;
2526
import com.intellij.psi.PsiAnnotationMemberValue;
2627
import com.intellij.psi.PsiArrayInitializerMemberValue;
@@ -311,18 +312,16 @@ private static boolean hasBuildMethod(@Nullable PsiType builderType, @NotNull Ps
311312
* Find all defined {@link org.mapstruct.Mapping#target()} for the given method
312313
*
313314
* @param method that needs to be checked
315+
* @param mapStructVersion the MapStruct project version
314316
*
315317
* @return see description
316318
*/
317-
public static Stream<String> findAllDefinedMappingTargets(@NotNull PsiMethod method) {
319+
public static Stream<String> findAllDefinedMappingTargets(@NotNull PsiMethod method,
320+
MapStructVersion mapStructVersion) {
318321
//TODO cache
319322
PsiAnnotation mappings = findAnnotation( method, true, MapstructUtil.MAPPINGS_ANNOTATION_FQN );
320-
Stream<PsiAnnotation> mappingsAnnotations;
321-
if ( mappings == null ) {
322-
mappingsAnnotations = Stream.of( method.getModifierList().getAnnotations() )
323-
.filter( TargetUtils::isMappingAnnotation );
324-
}
325-
else {
323+
Stream<PsiAnnotation> mappingsAnnotations = Stream.empty();
324+
if ( mappings != null ) {
326325
//TODO maybe there is a better way to do this, but currently I don't have that much knowledge
327326
PsiNameValuePair mappingsValue = findDeclaredAttribute( mappings, null );
328327
if ( mappingsValue != null && mappingsValue.getValue() instanceof PsiArrayInitializerMemberValue ) {
@@ -334,18 +333,26 @@ public static Stream<String> findAllDefinedMappingTargets(@NotNull PsiMethod met
334333
else if ( mappingsValue != null && mappingsValue.getValue() instanceof PsiAnnotation ) {
335334
mappingsAnnotations = Stream.of( (PsiAnnotation) mappingsValue.getValue() );
336335
}
337-
else {
338-
mappingsAnnotations = Stream.empty();
339-
}
340336
}
341337

342-
return mappingsAnnotations
343-
.map( psiAnnotation -> psiAnnotation.findDeclaredAttributeValue( "target" ) )
338+
Stream<PsiAnnotation> mappingAnnotations = findMappingAnnotations( method, mapStructVersion );
339+
340+
return Stream.concat( mappingAnnotations, mappingsAnnotations )
341+
.map( psiAnnotation -> AnnotationUtil.getDeclaredStringAttributeValue( psiAnnotation, "target" ) )
344342
.filter( Objects::nonNull )
345-
.map( ElementManipulators::getValueText )
346343
.filter( s -> !s.isEmpty() );
347344
}
348345

346+
private static Stream<PsiAnnotation> findMappingAnnotations(@NotNull PsiMethod method,
347+
MapStructVersion mapStructVersion) {
348+
if ( mapStructVersion.isConstructorSupported() ) {
349+
// Meta annotations support was added when constructor support was added
350+
return MetaAnnotationUtil.findMetaAnnotations( method, Collections.singleton( MAPPING_ANNOTATION_FQN ) );
351+
}
352+
return Stream.of( method.getModifierList().getAnnotations() )
353+
.filter( TargetUtils::isMappingAnnotation );
354+
}
355+
349356
/**
350357
* @param memberValue that needs to be checked
351358
*
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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.intellij.inspection;
7+
8+
import java.util.List;
9+
10+
import com.intellij.codeInsight.intention.IntentionAction;
11+
import org.jetbrains.annotations.NotNull;
12+
13+
import static org.assertj.core.api.Assertions.assertThat;
14+
15+
/**
16+
* @author Filip Hrisafov
17+
*/
18+
public class UnmappedTargetPropertiesWithMetaAnnotationInspectionTest extends BaseInspectionTest {
19+
20+
@NotNull
21+
@Override
22+
protected Class<UnmappedTargetPropertiesInspection> getInspection() {
23+
return UnmappedTargetPropertiesInspection.class;
24+
}
25+
26+
@Override
27+
protected void setUp() throws Exception {
28+
super.setUp();
29+
myFixture.copyFileToProject(
30+
"UnmappedTargetPropertiesData.java",
31+
"org/example/data/UnmappedConstructorTargetPropertiesData.java"
32+
);
33+
34+
myFixture.copyFileToProject(
35+
"MetaMappingIgnoreTestName.java",
36+
"org/example/data/IgnoreTestName.java"
37+
);
38+
}
39+
40+
public void testUnmappedTargetPropertiesWithMetaAnnotation() {
41+
doTest();
42+
String testName = getTestName( false );
43+
List<IntentionAction> allQuickFixes = myFixture.getAllQuickFixes();
44+
45+
assertThat( allQuickFixes )
46+
.extracting( IntentionAction::getText )
47+
.as( "Intent Text" )
48+
.containsExactly(
49+
"Ignore unmapped target property: 'moreTarget'",
50+
"Add unmapped target property: 'moreTarget'"
51+
);
52+
53+
allQuickFixes.forEach( myFixture::launchAction );
54+
myFixture.checkResultByFile( testName + "_after.java" );
55+
}
56+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
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.example.data;
7+
8+
import org.mapstruct.Mapping;
9+
import java.lang.annotation.ElementType;
10+
import java.lang.annotation.Target;
11+
import java.lang.annotation.Retention;
12+
import java.lang.annotation.RetentionPolicy;
13+
14+
@Mapping(target = "testName", ignore = true)
15+
@Retention(RetentionPolicy.SOURCE)
16+
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
17+
public @interface IgnoreTestName { }
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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+
7+
import org.mapstruct.Context;
8+
import org.mapstruct.Mapper;
9+
import org.mapstruct.Mapping;
10+
import org.mapstruct.MappingTarget;
11+
import org.mapstruct.Mappings;
12+
import org.example.data.IgnoreTestName;
13+
import org.example.data.UnmappedTargetPropertiesData.Target;
14+
import org.example.data.UnmappedTargetPropertiesData.Source;
15+
16+
@Mapper
17+
interface SingleMappingsMapper {
18+
19+
@Mappings({
20+
@Mapping(target = "moreTarget", source = "moreSource")
21+
})
22+
@IgnoreTestName
23+
Target map(Source source);
24+
}
25+
26+
@Mapper
27+
interface SingleMappingMapper {
28+
29+
@IgnoreTestName
30+
Target <warning descr="Unmapped target property: moreTarget">map</warning>(Source source);
31+
}
32+
33+
@Mapper
34+
interface AllMappingMapper {
35+
36+
@Mapping(target = "moreTarget", source = "moreSource")
37+
@IgnoreTestName
38+
Target mapWithAllMapping(Source source);
39+
}
40+
41+
@Mapper
42+
interface UpdateMapper {
43+
44+
@Mapping(target = "moreTarget", source = "moreSource")
45+
@IgnoreTestName
46+
void update(@MappingTarget Target target, Source source);
47+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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+
7+
import org.mapstruct.Context;
8+
import org.mapstruct.Mapper;
9+
import org.mapstruct.Mapping;
10+
import org.mapstruct.MappingTarget;
11+
import org.mapstruct.Mappings;
12+
import org.example.data.IgnoreTestName;
13+
import org.example.data.UnmappedTargetPropertiesData.Target;
14+
import org.example.data.UnmappedTargetPropertiesData.Source;
15+
16+
@Mapper
17+
interface SingleMappingsMapper {
18+
19+
@Mappings({
20+
@Mapping(target = "moreTarget", source = "moreSource")
21+
})
22+
@IgnoreTestName
23+
Target map(Source source);
24+
}
25+
26+
@Mapper
27+
interface SingleMappingMapper {
28+
29+
@Mapping(target = "moreTarget", source = "")
30+
@Mapping(target = "moreTarget", ignore = true)
31+
@IgnoreTestName
32+
Target map(Source source);
33+
}
34+
35+
@Mapper
36+
interface AllMappingMapper {
37+
38+
@Mapping(target = "moreTarget", source = "moreSource")
39+
@IgnoreTestName
40+
Target mapWithAllMapping(Source source);
41+
}
42+
43+
@Mapper
44+
interface UpdateMapper {
45+
46+
@Mapping(target = "moreTarget", source = "moreSource")
47+
@IgnoreTestName
48+
void update(@MappingTarget Target target, Source source);
49+
}

0 commit comments

Comments
 (0)