From 7ce36a42ce5d48d34b7adce99a76c3f1273c423f Mon Sep 17 00:00:00 2001
From: hduelme
Date: Wed, 13 Dec 2023 23:22:09 +0100
Subject: [PATCH 1/7] support Map as MappingSource
---
.../FromMapMappingMapTypeInspection.java | 190 ++++++++++++++++++
.../UnmappedTargetPropertiesInspection.java | 37 +---
.../mapstruct/intellij/util/SourceUtils.java | 21 ++
.../mapstruct/intellij/util/TargetUtils.java | 28 +++
src/main/resources/META-INF/plugin.xml | 8 +
.../FromMapMappingInspection.html | 33 +++
.../messages/MapStructBundle.properties | 7 +
.../FromMapMappingMapTypeInspectionTest.java | 61 ++++++
...argetPropertiesWithFromMapMappingTest.java | 43 ++++
...FromMapMappingMapTypeInspectionRawMap.java | 76 +++++++
...pMappingMapTypeInspectionRawMap_after.java | 76 +++++++
...pMappingMapTypeInspectionWrongKeyType.java | 76 +++++++
...ngMapTypeInspectionWrongKeyType_after.java | 76 +++++++
...pedTargetPropertiesWithFromMapMapping.java | 25 +++
14 files changed, 726 insertions(+), 31 deletions(-)
create mode 100644 src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java
create mode 100644 src/main/resources/inspectionDescriptions/FromMapMappingInspection.html
create mode 100644 src/test/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspectionTest.java
create mode 100644 src/test/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesWithFromMapMappingTest.java
create mode 100644 testData/inspection/FromMapMappingMapTypeInspectionRawMap.java
create mode 100644 testData/inspection/FromMapMappingMapTypeInspectionRawMap_after.java
create mode 100644 testData/inspection/FromMapMappingMapTypeInspectionWrongKeyType.java
create mode 100644 testData/inspection/FromMapMappingMapTypeInspectionWrongKeyType_after.java
create mode 100644 testData/inspection/UnmappedTargetPropertiesWithFromMapMapping.java
diff --git a/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java b/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java
new file mode 100644
index 00000000..36f71edd
--- /dev/null
+++ b/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.intellij.inspection;
+
+import com.intellij.codeInspection.LocalQuickFixOnPsiElement;
+import com.intellij.codeInspection.ProblemsHolder;
+import com.intellij.codeInspection.util.IntentionFamilyName;
+import com.intellij.codeInspection.util.IntentionName;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.JavaElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiElementVisitor;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.impl.source.PsiClassReferenceType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.mapstruct.intellij.MapStructBundle;
+import org.mapstruct.intellij.util.MapstructUtil;
+
+import static com.intellij.psi.PsiElementFactory.getInstance;
+import static org.mapstruct.intellij.util.SourceUtils.getFromMapMappingParameter;
+import static org.mapstruct.intellij.util.TargetUtils.getTargetType;
+
+/**
+ * @author hduelme
+ */
+public class FromMapMappingMapTypeInspection extends InspectionBase {
+
+ @NotNull
+ @Override
+ PsiElementVisitor buildVisitorInternal(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
+ return new MyJavaElementVisitor( holder );
+ }
+
+ private static class MyJavaElementVisitor extends JavaElementVisitor {
+ private final ProblemsHolder holder;
+
+ private MyJavaElementVisitor(ProblemsHolder holder) {
+ this.holder = holder;
+ }
+
+ @Override
+ public void visitMethod(@NotNull PsiMethod method) {
+ super.visitMethod( method );
+
+ if (!MapstructUtil.isMapper( method.getContainingClass() ) ) {
+ return;
+ }
+
+ PsiType targetType = getTargetType( method );
+ if (targetType == null) {
+ return;
+ }
+
+ PsiParameter fromMapMappingParameter = getFromMapMappingParameter( method );
+ PsiType[] parameters = getParameters( fromMapMappingParameter );
+ if ( parameters == null ) {
+ return;
+ }
+ if (parameters.length == 0) {
+ // handle raw type
+ holder.registerProblem( fromMapMappingParameter,
+ MapStructBundle.message( "inspection.wrong.map.mapping.map.type.raw" ),
+ new ReplaceByStringStringMapTypeFix( fromMapMappingParameter ) );
+ }
+ else if (parameters.length == 2) {
+ // only if both parameters of the map are set
+ PsiType keyParameter = parameters[0];
+ if ( !keyParameter.equalsToText( "java.lang.String" ) ) {
+ // handle wrong map key type
+ holder.registerProblem( fromMapMappingParameter,
+ MapStructBundle.message( "inspection.wrong.map.mapping.map.key" ),
+ new ReplaceMapKeyByStringTypeFix( fromMapMappingParameter ) );
+ }
+ }
+ }
+
+ @Nullable
+ private static PsiType[] getParameters(@Nullable PsiParameter fromMapMappingParameter) {
+ if (fromMapMappingParameter == null ||
+ !(fromMapMappingParameter.getType() instanceof PsiClassReferenceType)) {
+ return null;
+ }
+ return ((PsiClassReferenceType) fromMapMappingParameter.getType()).getParameters();
+ }
+
+ private static class ReplaceByStringStringMapTypeFix extends LocalQuickFixOnPsiElement {
+
+ private final String text;
+
+ private ReplaceByStringStringMapTypeFix(@NotNull PsiParameter element) {
+ super( element );
+ this.text = MapStructBundle.message( "inspection.wrong.map.mapping.map.type.raw.set.default",
+ element.getType().getPresentableText() );
+ }
+
+ @Override
+ public @IntentionName @NotNull String getText() {
+ return text;
+ }
+
+ @Override
+ public boolean isAvailable(@NotNull Project project, @NotNull PsiFile file,
+ @NotNull PsiElement startElement, @NotNull PsiElement endElement) {
+ if (!super.isAvailable( project, file, startElement, endElement ) ) {
+ return false;
+ }
+ if (startElement instanceof PsiParameter) {
+ PsiParameter parameter = (PsiParameter) startElement;
+ PsiType[] parameters = getParameters( parameter );
+ return parameters != null && parameters.length == 0;
+ }
+ return false;
+ }
+
+ @Override
+ public void invoke(@NotNull Project project, @NotNull PsiFile psiFile, @NotNull PsiElement psiElement,
+ @NotNull PsiElement psiElement1) {
+ if (psiElement instanceof PsiParameter) {
+ String mapText = psiElement.getText();
+ String prefix = mapText.substring( 0, mapText.indexOf( ' ' ) );
+ String end = mapText.substring( mapText.lastIndexOf( ' ' ) );
+ String result = prefix + "" + end;
+ psiElement.replace( getInstance( project ).createParameterFromText( result, psiElement ) );
+ }
+ }
+
+ @Override
+ public @IntentionFamilyName @NotNull String getFamilyName() {
+ return MapStructBundle.message( "intention.wrong.map.mapping.map.type.raw" );
+ }
+ }
+
+ private static class ReplaceMapKeyByStringTypeFix extends LocalQuickFixOnPsiElement {
+
+ private final String text;
+
+ private ReplaceMapKeyByStringTypeFix(@NotNull PsiParameter element) {
+ super( element );
+ this.text = MapStructBundle.message( "inspection.wrong.map.mapping.map.key.change.to.String" );
+ }
+
+ @Override
+ public @IntentionName @NotNull String getText() {
+ return text;
+ }
+
+ @Override
+ public boolean isAvailable(@NotNull Project project, @NotNull PsiFile file,
+ @NotNull PsiElement startElement, @NotNull PsiElement endElement) {
+ if (!super.isAvailable( project, file, startElement, endElement ) ) {
+ return false;
+ }
+ if (startElement instanceof PsiParameter) {
+ PsiParameter parameter = (PsiParameter) startElement;
+ PsiType[] parameters = getParameters( parameter );
+ if (parameters == null || parameters.length != 2) {
+ return false;
+ }
+ return !parameters[0].equalsToText( "java.lang.String" );
+ }
+ return false;
+ }
+
+ @Override
+ public void invoke(@NotNull Project project, @NotNull PsiFile psiFile, @NotNull PsiElement psiElement,
+ @NotNull PsiElement psiElement1) {
+ if (psiElement instanceof PsiParameter) {
+ String mapText = psiElement.getText();
+ String prefix = mapText.substring( 0, mapText.indexOf( '<' ) + 1 );
+ String end = mapText.substring( mapText.indexOf( ',' ) );
+ String result = prefix + "String" + end;
+ psiElement.replace( getInstance( project ).createParameterFromText( result, psiElement ) );
+ }
+ }
+
+ @Override
+ public @IntentionFamilyName @NotNull String getFamilyName() {
+ return MapStructBundle.message( "intention.wrong.map.mapping.map.key" );
+ }
+ }
+ }
+
+}
diff --git a/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java b/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java
index dcaa9677..7416df56 100644
--- a/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java
+++ b/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java
@@ -21,18 +21,15 @@
import com.intellij.psi.JavaElementVisitor;
import com.intellij.psi.JavaPsiFacade;
import com.intellij.psi.PsiAnnotation;
-import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiElementVisitor;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
-import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiModifierListOwner;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.annotations.Nullable;
import org.mapstruct.ReportingPolicy;
import org.mapstruct.intellij.MapStructBundle;
import org.mapstruct.intellij.settings.ProjectSettings;
@@ -44,14 +41,12 @@
import static org.mapstruct.intellij.inspection.inheritance.InheritConfigurationUtils.findInheritedTargetProperties;
import static org.mapstruct.intellij.util.MapstructAnnotationUtils.addMappingAnnotation;
import static org.mapstruct.intellij.util.MapstructAnnotationUtils.getUnmappedTargetPolicy;
-import static org.mapstruct.intellij.util.MapstructUtil.isInheritInverseConfiguration;
-import static org.mapstruct.intellij.util.MapstructUtil.isMapper;
-import static org.mapstruct.intellij.util.MapstructUtil.isMapperConfig;
import static org.mapstruct.intellij.util.SourceUtils.findAllSourceProperties;
+import static org.mapstruct.intellij.util.SourceUtils.isFromMapMapping;
import static org.mapstruct.intellij.util.TargetUtils.findAllDefinedMappingTargets;
import static org.mapstruct.intellij.util.TargetUtils.findAllSourcePropertiesForCurrentTarget;
import static org.mapstruct.intellij.util.TargetUtils.findAllTargetProperties;
-import static org.mapstruct.intellij.util.TargetUtils.getRelevantType;
+import static org.mapstruct.intellij.util.TargetUtils.getTargetType;
/**
* Inspection that checks if there are unmapped target properties.
@@ -91,6 +86,10 @@ public void visitMethod(PsiMethod method) {
return;
}
+ if ( isFromMapMapping( method ) ) {
+ return;
+ }
+
ReportingPolicy reportingPolicy = getUnmappedTargetPolicy( method );
if (reportingPolicy == ReportingPolicy.IGNORE) {
return;
@@ -185,30 +184,6 @@ private static boolean isBeanMappingIgnoreByDefault(PsiMethod method) {
return false;
}
- /**
- * @param method the method to be used
- *
- * @return the target class for the inspection, or {@code null} if no inspection needs to be performed
- */
- @Nullable
- private static PsiType getTargetType(PsiMethod method) {
- if ( !method.getModifierList().hasModifierProperty( PsiModifier.ABSTRACT ) ) {
- return null;
- }
-
- if ( isInheritInverseConfiguration( method ) ) {
- return null;
- }
- PsiClass containingClass = method.getContainingClass();
-
- if ( containingClass == null
- || method.getNameIdentifier() == null
- || !( isMapper( containingClass ) || isMapperConfig( containingClass ) ) ) {
- return null;
- }
- return getRelevantType( method );
- }
-
}
private static class UnmappedTargetPropertyFix extends LocalQuickFixOnPsiElement {
diff --git a/src/main/java/org/mapstruct/intellij/util/SourceUtils.java b/src/main/java/org/mapstruct/intellij/util/SourceUtils.java
index f78faf10..bc401d54 100644
--- a/src/main/java/org/mapstruct/intellij/util/SourceUtils.java
+++ b/src/main/java/org/mapstruct/intellij/util/SourceUtils.java
@@ -10,9 +10,11 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
+import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.intellij.codeInsight.AnnotationUtil;
@@ -211,4 +213,23 @@ else if ( methodName.startsWith( "is" ) && (
}
}
+
+ public static boolean isFromMapMapping(@NotNull PsiMethod method) {
+ return getFromMapMappingParameter( method ) != null;
+ }
+
+ @Nullable
+ public static PsiParameter getFromMapMappingParameter(@NotNull PsiMethod method) {
+ List sourceParameters = Arrays.stream( method.getParameterList().getParameters() )
+ .filter( MapstructUtil::isValidSourceParameter ).collect( Collectors.toList() );
+ if (sourceParameters.size() == 1) {
+ PsiParameter parameter = sourceParameters.get( 0 );
+ if (parameter != null && PsiType.getTypeByName( "java.util.Map", method.getProject(),
+ method.getResolveScope() ).isAssignableFrom( parameter.getType() ) ) {
+ return parameter;
+ }
+
+ }
+ return null;
+ }
}
diff --git a/src/main/java/org/mapstruct/intellij/util/TargetUtils.java b/src/main/java/org/mapstruct/intellij/util/TargetUtils.java
index 33fa861e..4c42d55f 100644
--- a/src/main/java/org/mapstruct/intellij/util/TargetUtils.java
+++ b/src/main/java/org/mapstruct/intellij/util/TargetUtils.java
@@ -29,6 +29,7 @@
import com.intellij.psi.PsiJavaCodeReferenceElement;
import com.intellij.psi.PsiMember;
import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiModifier;
import com.intellij.psi.PsiModifierListOwner;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiSubstitutor;
@@ -45,6 +46,9 @@
import static org.mapstruct.intellij.util.MapstructUtil.MAPPER_ANNOTATION_FQN;
import static org.mapstruct.intellij.util.MapstructUtil.canDescendIntoType;
import static org.mapstruct.intellij.util.MapstructUtil.isFluentSetter;
+import static org.mapstruct.intellij.util.MapstructUtil.isInheritInverseConfiguration;
+import static org.mapstruct.intellij.util.MapstructUtil.isMapper;
+import static org.mapstruct.intellij.util.MapstructUtil.isMapperConfig;
import static org.mapstruct.intellij.util.MapstructUtil.publicFields;
/**
@@ -433,4 +437,28 @@ public static Set findAllTargetProperties(@NotNull PsiType targetType, M
return publicWriteAccessors( targetType, mapStructVersion, mappingMethod ).keySet();
}
+ /**
+ * @param method the method to be used
+ *
+ * @return the target class for the inspection, or {@code null} if no inspection needs to be performed
+ */
+ @Nullable
+ public static PsiType getTargetType( @NotNull PsiMethod method) {
+ if ( !method.getModifierList().hasModifierProperty( PsiModifier.ABSTRACT ) ) {
+ return null;
+ }
+
+ if ( isInheritInverseConfiguration( method ) ) {
+ return null;
+ }
+ PsiClass containingClass = method.getContainingClass();
+
+ if ( containingClass == null
+ || method.getNameIdentifier() == null
+ || !( isMapper( containingClass ) || isMapperConfig( containingClass ) ) ) {
+ return null;
+ }
+ return getRelevantType( method );
+ }
+
}
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index b35a58f4..62863a55 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -112,6 +112,14 @@
key="inspection.java.expression.unnecessary.whitespace.title"
shortName="JavaExpressionUnnecessaryWhitespaces"
implementationClass="org.mapstruct.intellij.inspection.JavaExpressionUnnecessaryWhitespacesInspector"/>
+
diff --git a/src/main/resources/inspectionDescriptions/FromMapMappingInspection.html b/src/main/resources/inspectionDescriptions/FromMapMappingInspection.html
new file mode 100644
index 00000000..c8cd215c
--- /dev/null
+++ b/src/main/resources/inspectionDescriptions/FromMapMappingInspection.html
@@ -0,0 +1,33 @@
+
+
+
+ This inspection reports when a raw Map
or a Map
where the key isn't a String
+ is used for mapping to Bean.
+
+
+
+//wrong
+@Mapper
+public interface EmployeeMapper {
+
+ Employee toEmployee(Map map);
+
+ EmployeeDto toEmployeeDto(Map<Long, ?> map);
+}
+
+
+
+
+//correct
+@Mapper
+public interface EmployeeMapper {
+
+ Employee toEmployee(Map<String String> map);
+
+ EmployeeDto toEmployeeDto(Map<String, ?> map);
+}
+
+
+
+
+
\ No newline at end of file
diff --git a/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties b/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties
index 5f175e06..03817c44 100644
--- a/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties
+++ b/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties
@@ -19,6 +19,11 @@ inspection.not.null.checkable.property.source.used.with.default.property.title=C
inspection.java.expression.unnecessary.whitespace=Unnecessary whitespaces {0} {1}
inspection.java.expression.remove.unnecessary.whitespace=Remove unnecessary whitespaces {0} {1}
inspection.java.expression.unnecessary.whitespace.title=Unnecessary whitespaces before or after Java expression
+inspection.wrong.map.mapping.map.type=Map type is raw or key type is not string for mapping Map to Bean
+inspection.wrong.map.mapping.map.type.raw=Raw map used for mapping Map to Bean
+inspection.wrong.map.mapping.map.type.raw.set.default=Replace {0} with {0}
+inspection.wrong.map.mapping.map.key=Key must be of type String for mapping Map to Bean
+inspection.wrong.map.mapping.map.key.change.to.String=Change key type to String
intention.add.ignore.all.unmapped.target.properties=Add ignore all unmapped target properties
intention.add.ignore.unmapped.target.property=Add ignore unmapped target property
intention.add.unmapped.target.property=Add unmapped target property
@@ -27,6 +32,8 @@ intention.more.than.one.source.property=Only use one source property
intention.more.than.one.default.source.property=Only use one default source property
intention.not.null.checkable.property.source.used.with.default.property=Remove default properties
intention.java.expression.remove.unnecessary.whitespace=Remove unnecessary whitespaces
+intention.wrong.map.mapping.map.type.raw=Add type to Map for mapping Map to Bean
+intention.wrong.map.mapping.map.key=Use Map with key of type String for mapping Map to Bean
plugin.settings.title=MapStruct
plugin.settings.quickFix.title=Quick fix properties
plugin.settings.quickFix.preferSourceBeforeTargetInMapping=Prefer source before target in @Mapping
diff --git a/src/test/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspectionTest.java b/src/test/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspectionTest.java
new file mode 100644
index 00000000..bc223234
--- /dev/null
+++ b/src/test/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspectionTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.intellij.inspection;
+
+import com.intellij.codeInsight.intention.IntentionAction;
+import com.intellij.codeInspection.LocalInspectionTool;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author hduelme
+ */
+public class FromMapMappingMapTypeInspectionTest extends BaseInspectionTest {
+
+ @Override
+ protected @NotNull Class extends LocalInspectionTool> getInspection() {
+ return FromMapMappingMapTypeInspection.class;
+ }
+
+ public void testFromMapMappingMapTypeInspectionRawMap() {
+ doTest();
+ String testName = getTestName( false );
+ List allQuickFixes = myFixture.getAllQuickFixes();
+
+ assertThat( allQuickFixes )
+ .extracting( IntentionAction::getText )
+ .as( "Intent Text" )
+ .containsExactly(
+ "Replace Map with Map",
+ "Replace HashMap with HashMap",
+ "Replace Map with Map",
+ "Replace HashMap with HashMap"
+ );
+
+ allQuickFixes.forEach( myFixture::launchAction );
+ myFixture.checkResultByFile( testName + "_after.java" );
+ }
+
+ public void testFromMapMappingMapTypeInspectionWrongKeyType() {
+ doTest();
+ String testName = getTestName( false );
+ List allQuickFixes = myFixture.getAllQuickFixes();
+
+ assertThat( allQuickFixes )
+ .extracting( IntentionAction::getText )
+ .as( "Intent Text" )
+ .containsExactly(
+ "Change key type to String", "Change key type to String",
+ "Change key type to String", "Change key type to String"
+ );
+
+ allQuickFixes.forEach( myFixture::launchAction );
+ myFixture.checkResultByFile( testName + "_after.java" );
+ }
+}
diff --git a/src/test/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesWithFromMapMappingTest.java b/src/test/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesWithFromMapMappingTest.java
new file mode 100644
index 00000000..54bbd6df
--- /dev/null
+++ b/src/test/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesWithFromMapMappingTest.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+package org.mapstruct.intellij.inspection;
+
+import com.intellij.codeInsight.intention.IntentionAction;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * @author hduelme
+ */
+public class UnmappedTargetPropertiesWithFromMapMappingTest extends BaseInspectionTest {
+
+ @NotNull
+ @Override
+ protected Class getInspection() {
+ return UnmappedTargetPropertiesInspection.class;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ myFixture.copyFileToProject(
+ "UnmappedTargetPropertiesData.java",
+ "org/example/data/UnmappedTargetPropertiesData.java"
+ );
+ }
+
+ /**
+ * Tests if no unmapped target properties warnings are generated when source is map
+ */
+ public void testUnmappedTargetPropertiesWithFromMapMapping() {
+ doTest();
+ List allQuickFixes = myFixture.getAllQuickFixes();
+ assertThat( allQuickFixes ).isEmpty();
+ }
+}
diff --git a/testData/inspection/FromMapMappingMapTypeInspectionRawMap.java b/testData/inspection/FromMapMappingMapTypeInspectionRawMap.java
new file mode 100644
index 00000000..2ee2e297
--- /dev/null
+++ b/testData/inspection/FromMapMappingMapTypeInspectionRawMap.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Context;
+import org.mapstruct.Mapper;
+import org.mapstruct.MappingTarget;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Target {
+
+ private String name;
+ private String lastName;
+ private String city;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
+
+interface NotMapStructMapper {
+
+ Target map(Map source);
+}
+
+@Mapper
+interface NoMappingMapper {
+
+ Target map(Map source);
+
+ Target map(HashMap source);
+}
+
+@Mapper
+interface MultiSourceMappingsMapper {
+
+ Target mapWithAllMapping(Map source, String moreTarget, String testName);
+}
+
+@Mapper
+interface UpdateMapper {
+
+ void update(@MappingTarget Target target, Map source);
+
+ void update(@MappingTarget Target target, HashMap source);
+}
+
+@Mapper
+interface MultiSourceUpdateMapper {
+
+ void update(@MappingTarget Target moreTarget, Map source, String testName, @Context String matching);
+}
+
+@Mapper
+interface DefaultMapper {
+
+ default Target map(Map source) {
+ return null;
+ }
+}
+
+@Mapper
+abstract class AbstractMapperWithoutAbstractMethod {
+
+ protected Target map(Map source) {
+ return null;
+ }
+}
diff --git a/testData/inspection/FromMapMappingMapTypeInspectionRawMap_after.java b/testData/inspection/FromMapMappingMapTypeInspectionRawMap_after.java
new file mode 100644
index 00000000..52e12b61
--- /dev/null
+++ b/testData/inspection/FromMapMappingMapTypeInspectionRawMap_after.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Context;
+import org.mapstruct.Mapper;
+import org.mapstruct.MappingTarget;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Target {
+
+ private String name;
+ private String lastName;
+ private String city;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
+
+interface NotMapStructMapper {
+
+ Target map(Map source);
+}
+
+@Mapper
+interface NoMappingMapper {
+
+ Target map(Map source);
+
+ Target map(HashMap source);
+}
+
+@Mapper
+interface MultiSourceMappingsMapper {
+
+ Target mapWithAllMapping(Map source, String moreTarget, String testName);
+}
+
+@Mapper
+interface UpdateMapper {
+
+ void update(@MappingTarget Target target, Map source);
+
+ void update(@MappingTarget Target target, HashMap source);
+}
+
+@Mapper
+interface MultiSourceUpdateMapper {
+
+ void update(@MappingTarget Target moreTarget, Map source, String testName, @Context String matching);
+}
+
+@Mapper
+interface DefaultMapper {
+
+ default Target map(Map source) {
+ return null;
+ }
+}
+
+@Mapper
+abstract class AbstractMapperWithoutAbstractMethod {
+
+ protected Target map(Map source) {
+ return null;
+ }
+}
diff --git a/testData/inspection/FromMapMappingMapTypeInspectionWrongKeyType.java b/testData/inspection/FromMapMappingMapTypeInspectionWrongKeyType.java
new file mode 100644
index 00000000..9e7a082f
--- /dev/null
+++ b/testData/inspection/FromMapMappingMapTypeInspectionWrongKeyType.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Context;
+import org.mapstruct.Mapper;
+import org.mapstruct.MappingTarget;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Target {
+
+ private String name;
+ private String lastName;
+ private String city;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
+
+interface NotMapStructMapper {
+
+ Target map(Map source);
+}
+
+@Mapper
+interface NoMappingMapper {
+
+ Target map(Map source);
+
+ Target map(HashMap source);
+}
+
+@Mapper
+interface MultiSourceMappingsMapper {
+
+ Target mapWithAllMapping(Map source, String moreTarget, String testName);
+}
+
+@Mapper
+interface UpdateMapper {
+
+ void update(@MappingTarget Target target, Map source);
+
+ void update(@MappingTarget Target target, HashMap source);
+}
+
+@Mapper
+interface MultiSourceUpdateMapper {
+
+ void update(@MappingTarget Target moreTarget, Map source, String testName, @Context String matching);
+}
+
+@Mapper
+interface DefaultMapper {
+
+ default Target map(Map source) {
+ return null;
+ }
+}
+
+@Mapper
+abstract class AbstractMapperWithoutAbstractMethod {
+
+ protected Target map(Map source) {
+ return null;
+ }
+}
diff --git a/testData/inspection/FromMapMappingMapTypeInspectionWrongKeyType_after.java b/testData/inspection/FromMapMappingMapTypeInspectionWrongKeyType_after.java
new file mode 100644
index 00000000..138d2c2f
--- /dev/null
+++ b/testData/inspection/FromMapMappingMapTypeInspectionWrongKeyType_after.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Context;
+import org.mapstruct.Mapper;
+import org.mapstruct.MappingTarget;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Target {
+
+ private String name;
+ private String lastName;
+ private String city;
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+}
+
+interface NotMapStructMapper {
+
+ Target map(Map source);
+}
+
+@Mapper
+interface NoMappingMapper {
+
+ Target map(Map source);
+
+ Target map(HashMap source);
+}
+
+@Mapper
+interface MultiSourceMappingsMapper {
+
+ Target mapWithAllMapping(Map source, String moreTarget, String testName);
+}
+
+@Mapper
+interface UpdateMapper {
+
+ void update(@MappingTarget Target target, Map source);
+
+ void update(@MappingTarget Target target, HashMap source);
+}
+
+@Mapper
+interface MultiSourceUpdateMapper {
+
+ void update(@MappingTarget Target moreTarget, Map source, String testName, @Context String matching);
+}
+
+@Mapper
+interface DefaultMapper {
+
+ default Target map(Map source) {
+ return null;
+ }
+}
+
+@Mapper
+abstract class AbstractMapperWithoutAbstractMethod {
+
+ protected Target map(Map source) {
+ return null;
+ }
+}
diff --git a/testData/inspection/UnmappedTargetPropertiesWithFromMapMapping.java b/testData/inspection/UnmappedTargetPropertiesWithFromMapMapping.java
new file mode 100644
index 00000000..3f662a2e
--- /dev/null
+++ b/testData/inspection/UnmappedTargetPropertiesWithFromMapMapping.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Context;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.Mappings;
+import org.example.data.UnmappedTargetPropertiesData.Target;
+
+import java.util.Map;
+
+@Mapper
+interface SinglMapper {
+
+ Target map(Map source);
+}
+@Mapper
+abstract class AbstractMapperWitAbstractMethod {
+
+ abstract Target map(Map source);
+}
From 02b12a6d8b03b18257afd54e9f6752eee56526a1 Mon Sep 17 00:00:00 2001
From: hduelme
Date: Wed, 13 Dec 2023 23:42:43 +0100
Subject: [PATCH 2/7] use getSourceParameters
---
.../java/org/mapstruct/intellij/util/SourceUtils.java | 10 +++-------
1 file changed, 3 insertions(+), 7 deletions(-)
diff --git a/src/main/java/org/mapstruct/intellij/util/SourceUtils.java b/src/main/java/org/mapstruct/intellij/util/SourceUtils.java
index bc401d54..7b11ccb5 100644
--- a/src/main/java/org/mapstruct/intellij/util/SourceUtils.java
+++ b/src/main/java/org/mapstruct/intellij/util/SourceUtils.java
@@ -10,11 +10,9 @@
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
-import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
-import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.intellij.codeInsight.AnnotationUtil;
@@ -220,15 +218,13 @@ public static boolean isFromMapMapping(@NotNull PsiMethod method) {
@Nullable
public static PsiParameter getFromMapMappingParameter(@NotNull PsiMethod method) {
- List sourceParameters = Arrays.stream( method.getParameterList().getParameters() )
- .filter( MapstructUtil::isValidSourceParameter ).collect( Collectors.toList() );
- if (sourceParameters.size() == 1) {
- PsiParameter parameter = sourceParameters.get( 0 );
+ PsiParameter[] sourceParameters = getSourceParameters( method );
+ if (sourceParameters.length == 1) {
+ PsiParameter parameter = sourceParameters[0];
if (parameter != null && PsiType.getTypeByName( "java.util.Map", method.getProject(),
method.getResolveScope() ).isAssignableFrom( parameter.getType() ) ) {
return parameter;
}
-
}
return null;
}
From 0a785e59726541295986d6594912b2878fea4cb3 Mon Sep 17 00:00:00 2001
From: hduelme
Date: Wed, 27 Dec 2023 16:57:28 +0100
Subject: [PATCH 3/7] fix typo
---
.../inspection/UnmappedTargetPropertiesWithFromMapMapping.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/testData/inspection/UnmappedTargetPropertiesWithFromMapMapping.java b/testData/inspection/UnmappedTargetPropertiesWithFromMapMapping.java
index 3f662a2e..eb2dc551 100644
--- a/testData/inspection/UnmappedTargetPropertiesWithFromMapMapping.java
+++ b/testData/inspection/UnmappedTargetPropertiesWithFromMapMapping.java
@@ -14,7 +14,7 @@
import java.util.Map;
@Mapper
-interface SinglMapper {
+interface SingleMapper {
Target map(Map source);
}
From 9342d1e12a7afccd3c9427b6fc6c799c63d3fc56 Mon Sep 17 00:00:00 2001
From: hduelme
Date: Wed, 27 Dec 2023 16:58:59 +0100
Subject: [PATCH 4/7] use lowercase s
---
.../intellij/inspection/FromMapMappingMapTypeInspection.java | 2 +-
.../org/mapstruct/intellij/messages/MapStructBundle.properties | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java b/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java
index 36f71edd..00b5f466 100644
--- a/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java
+++ b/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java
@@ -143,7 +143,7 @@ private static class ReplaceMapKeyByStringTypeFix extends LocalQuickFixOnPsiElem
private ReplaceMapKeyByStringTypeFix(@NotNull PsiParameter element) {
super( element );
- this.text = MapStructBundle.message( "inspection.wrong.map.mapping.map.key.change.to.String" );
+ this.text = MapStructBundle.message( "inspection.wrong.map.mapping.map.key.change.to.string" );
}
@Override
diff --git a/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties b/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties
index 03817c44..98252a11 100644
--- a/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties
+++ b/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties
@@ -23,7 +23,7 @@ inspection.wrong.map.mapping.map.type=Map type is raw or key type is not string
inspection.wrong.map.mapping.map.type.raw=Raw map used for mapping Map to Bean
inspection.wrong.map.mapping.map.type.raw.set.default=Replace {0} with {0}
inspection.wrong.map.mapping.map.key=Key must be of type String for mapping Map to Bean
-inspection.wrong.map.mapping.map.key.change.to.String=Change key type to String
+inspection.wrong.map.mapping.map.key.change.to.string=Change key type to String
intention.add.ignore.all.unmapped.target.properties=Add ignore all unmapped target properties
intention.add.ignore.unmapped.target.property=Add ignore unmapped target property
intention.add.unmapped.target.property=Add unmapped target property
From 0e21cc2e2c6923bdc56847a5883db503ab8fec1b Mon Sep 17 00:00:00 2001
From: hduelme
Date: Wed, 27 Dec 2023 17:00:38 +0100
Subject: [PATCH 5/7] remove unused intention
---
.../org/mapstruct/intellij/messages/MapStructBundle.properties | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties b/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties
index 98252a11..c7ed3a12 100644
--- a/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties
+++ b/src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties
@@ -27,7 +27,6 @@ inspection.wrong.map.mapping.map.key.change.to.string=Change key type to String
intention.add.ignore.all.unmapped.target.properties=Add ignore all unmapped target properties
intention.add.ignore.unmapped.target.property=Add ignore unmapped target property
intention.add.unmapped.target.property=Add unmapped target property
-intention.no.source.property=Add one source property
intention.more.than.one.source.property=Only use one source property
intention.more.than.one.default.source.property=Only use one default source property
intention.not.null.checkable.property.source.used.with.default.property=Remove default properties
From db0854f271f1b631d7d964026b63f086845776da Mon Sep 17 00:00:00 2001
From: hduelme
Date: Wed, 27 Dec 2023 17:18:30 +0100
Subject: [PATCH 6/7] add map to map mapping no inspection test
---
.../FromMapMappingMapTypeInspectionTest.java | 6 ++++
...romMapMappingMapTypeToMapNoInspection.java | 35 +++++++++++++++++++
2 files changed, 41 insertions(+)
create mode 100644 testData/inspection/FromMapMappingMapTypeToMapNoInspection.java
diff --git a/src/test/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspectionTest.java b/src/test/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspectionTest.java
index bc223234..3f0f8ae2 100644
--- a/src/test/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspectionTest.java
+++ b/src/test/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspectionTest.java
@@ -58,4 +58,10 @@ public void testFromMapMappingMapTypeInspectionWrongKeyType() {
allQuickFixes.forEach( myFixture::launchAction );
myFixture.checkResultByFile( testName + "_after.java" );
}
+
+ public void testFromMapMappingMapTypeToMapNoInspection() {
+ doTest();
+ List allQuickFixes = myFixture.getAllQuickFixes();
+ assertThat( allQuickFixes ).isEmpty();
+ }
}
diff --git a/testData/inspection/FromMapMappingMapTypeToMapNoInspection.java b/testData/inspection/FromMapMappingMapTypeToMapNoInspection.java
new file mode 100644
index 00000000..ec3011c2
--- /dev/null
+++ b/testData/inspection/FromMapMappingMapTypeToMapNoInspection.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.InheritInverseConfiguration;
+import org.mapstruct.MapMapping;
+import org.mapstruct.Mapper;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.factory.Mappers;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Date;
+
+@Mapper
+interface MapToMapMapper {
+
+ @MapMapping(valueDateFormat = "dd.MM.yyyy")
+ Map longDateMapToStringStringMap(Map source);
+
+ @InheritInverseConfiguration
+ Map stringStringMapToLongDateMap(Map source);
+
+ @MapMapping(valueDateFormat = "dd.MM.yyyy")
+ void stringStringMapToLongDateMapUsingTargetParameter(@MappingTarget Map target,
+ Map source);
+
+ @MapMapping(valueDateFormat = "dd.MM.yyyy")
+ Map stringStringMapToLongDateMapUsingTargetParameterAndReturn(Map source,
+ @MappingTarget Map target);
+
+ Map intIntToNumberNumberMap(Map source);
+}
\ No newline at end of file
From b38ac06b0bc63974d144b89930c721b548d71cf6 Mon Sep 17 00:00:00 2001
From: hduelme
Date: Thu, 28 Dec 2023 01:33:47 +0100
Subject: [PATCH 7/7] match mapstruct behavior
---
.../FromMapMappingMapTypeInspection.java | 48 +++++++++++-----
.../UnmappedTargetPropertiesInspection.java | 18 +++++-
.../mapstruct/intellij/util/SourceUtils.java | 19 ++-----
src/main/resources/META-INF/plugin.xml | 2 +-
.../FromMapMappingMapTypeInspectionTest.java | 6 ++
...argetPropertiesWithFromMapMappingTest.java | 35 ++++++++++++
...FromMapMappingMapTypeInspectionRawMap.java | 8 +--
...pMappingMapTypeInspectionWrongKeyType.java | 8 +--
...apTypeWithSourceParameterNoInspection.java | 57 +++++++++++++++++++
...pedTargetPropertiesWithFromMapMapping.java | 4 +-
...opertiesWithFromMapMappingMultiSource.java | 25 ++++++++
...FromMapMappingMultiSourceWrongKeyType.java | 25 ++++++++
12 files changed, 217 insertions(+), 38 deletions(-)
create mode 100644 testData/inspection/FromMapMappingMapTypeWithSourceParameterNoInspection.java
create mode 100644 testData/inspection/UnmappedTargetPropertiesWithFromMapMappingMultiSource.java
create mode 100644 testData/inspection/UnmappedTargetPropertiesWithFromMapMappingMultiSourceWrongKeyType.java
diff --git a/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java b/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java
index 00b5f466..e72f7f8f 100644
--- a/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java
+++ b/src/main/java/org/mapstruct/intellij/inspection/FromMapMappingMapTypeInspection.java
@@ -17,14 +17,19 @@
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiType;
-import com.intellij.psi.impl.source.PsiClassReferenceType;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mapstruct.intellij.MapStructBundle;
+import org.mapstruct.intellij.util.MapStructVersion;
import org.mapstruct.intellij.util.MapstructUtil;
+import java.util.Set;
+
import static com.intellij.psi.PsiElementFactory.getInstance;
-import static org.mapstruct.intellij.util.SourceUtils.getFromMapMappingParameter;
+import static org.mapstruct.intellij.util.MapstructUtil.getSourceParameters;
+import static org.mapstruct.intellij.util.SourceUtils.findAllDefinedMappingSources;
+import static org.mapstruct.intellij.util.SourceUtils.getGenericTypes;
+import static org.mapstruct.intellij.util.TargetUtils.findAllTargetProperties;
import static org.mapstruct.intellij.util.TargetUtils.getTargetType;
/**
@@ -35,14 +40,16 @@ public class FromMapMappingMapTypeInspection extends InspectionBase {
@NotNull
@Override
PsiElementVisitor buildVisitorInternal(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
- return new MyJavaElementVisitor( holder );
+ return new MyJavaElementVisitor( holder, MapstructUtil.resolveMapStructProjectVersion( holder.getFile() ) );
}
private static class MyJavaElementVisitor extends JavaElementVisitor {
private final ProblemsHolder holder;
+ private final MapStructVersion mapStructVersion;
- private MyJavaElementVisitor(ProblemsHolder holder) {
+ private MyJavaElementVisitor(ProblemsHolder holder, MapStructVersion mapStructVersion) {
this.holder = holder;
+ this.mapStructVersion = mapStructVersion;
}
@Override
@@ -59,8 +66,19 @@ public void visitMethod(@NotNull PsiMethod method) {
}
PsiParameter fromMapMappingParameter = getFromMapMappingParameter( method );
- PsiType[] parameters = getParameters( fromMapMappingParameter );
- if ( parameters == null ) {
+ if (fromMapMappingParameter == null) {
+ return;
+ }
+ PsiType[] parameters = getGenericTypes( fromMapMappingParameter );
+ if (parameters == null) {
+ return;
+ }
+ Set allTargetProperties = findAllTargetProperties( targetType, mapStructVersion, method );
+ if ( allTargetProperties.contains( fromMapMappingParameter.getName() ) ) {
+ return;
+ }
+ if ( findAllDefinedMappingSources( method, mapStructVersion )
+ .anyMatch( source -> fromMapMappingParameter.getName().equals( source ) ) ) {
return;
}
if (parameters.length == 0) {
@@ -82,12 +100,16 @@ else if (parameters.length == 2) {
}
@Nullable
- private static PsiType[] getParameters(@Nullable PsiParameter fromMapMappingParameter) {
- if (fromMapMappingParameter == null ||
- !(fromMapMappingParameter.getType() instanceof PsiClassReferenceType)) {
- return null;
+ private static PsiParameter getFromMapMappingParameter(@NotNull PsiMethod method) {
+ PsiParameter[] sourceParameters = getSourceParameters( method );
+ if (sourceParameters.length == 1) {
+ PsiParameter parameter = sourceParameters[0];
+ if (parameter != null && PsiType.getTypeByName( "java.util.Map", method.getProject(),
+ method.getResolveScope() ).isAssignableFrom( parameter.getType() ) ) {
+ return parameter;
+ }
}
- return ((PsiClassReferenceType) fromMapMappingParameter.getType()).getParameters();
+ return null;
}
private static class ReplaceByStringStringMapTypeFix extends LocalQuickFixOnPsiElement {
@@ -113,7 +135,7 @@ public boolean isAvailable(@NotNull Project project, @NotNull PsiFile file,
}
if (startElement instanceof PsiParameter) {
PsiParameter parameter = (PsiParameter) startElement;
- PsiType[] parameters = getParameters( parameter );
+ PsiType[] parameters = getGenericTypes( parameter );
return parameters != null && parameters.length == 0;
}
return false;
@@ -159,7 +181,7 @@ public boolean isAvailable(@NotNull Project project, @NotNull PsiFile file,
}
if (startElement instanceof PsiParameter) {
PsiParameter parameter = (PsiParameter) startElement;
- PsiType[] parameters = getParameters( parameter );
+ PsiType[] parameters = getGenericTypes( parameter );
if (parameters == null || parameters.length != 2) {
return false;
}
diff --git a/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java b/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java
index 7416df56..f01320cc 100644
--- a/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java
+++ b/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java
@@ -26,6 +26,7 @@
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifierListOwner;
+import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiType;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.Nls;
@@ -41,8 +42,9 @@
import static org.mapstruct.intellij.inspection.inheritance.InheritConfigurationUtils.findInheritedTargetProperties;
import static org.mapstruct.intellij.util.MapstructAnnotationUtils.addMappingAnnotation;
import static org.mapstruct.intellij.util.MapstructAnnotationUtils.getUnmappedTargetPolicy;
+import static org.mapstruct.intellij.util.MapstructUtil.getSourceParameters;
import static org.mapstruct.intellij.util.SourceUtils.findAllSourceProperties;
-import static org.mapstruct.intellij.util.SourceUtils.isFromMapMapping;
+import static org.mapstruct.intellij.util.SourceUtils.getGenericTypes;
import static org.mapstruct.intellij.util.TargetUtils.findAllDefinedMappingTargets;
import static org.mapstruct.intellij.util.TargetUtils.findAllSourcePropertiesForCurrentTarget;
import static org.mapstruct.intellij.util.TargetUtils.findAllTargetProperties;
@@ -184,6 +186,20 @@ private static boolean isBeanMappingIgnoreByDefault(PsiMethod method) {
return false;
}
+ private static boolean isFromMapMapping(@NotNull PsiMethod method) {
+ PsiParameter[] sourceParameters = getSourceParameters( method );
+ for (PsiParameter parameter : sourceParameters) {
+ if (parameter != null && PsiType.getTypeByName( "java.util.Map", method.getProject(),
+ method.getResolveScope() ).isAssignableFrom( parameter.getType() ) ) {
+ PsiType[] generics = getGenericTypes( parameter );
+ if (generics != null && generics.length > 0) {
+ return generics[0].equalsToText( "java.lang.String" );
+ }
+ }
+ }
+ return false;
+ }
+
}
private static class UnmappedTargetPropertyFix extends LocalQuickFixOnPsiElement {
diff --git a/src/main/java/org/mapstruct/intellij/util/SourceUtils.java b/src/main/java/org/mapstruct/intellij/util/SourceUtils.java
index 7b11ccb5..4f918ce4 100644
--- a/src/main/java/org/mapstruct/intellij/util/SourceUtils.java
+++ b/src/main/java/org/mapstruct/intellij/util/SourceUtils.java
@@ -25,6 +25,7 @@
import com.intellij.psi.PsiRecordComponent;
import com.intellij.psi.PsiSubstitutor;
import com.intellij.psi.PsiType;
+import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.util.PsiUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -212,20 +213,12 @@ else if ( methodName.startsWith( "is" ) && (
}
- public static boolean isFromMapMapping(@NotNull PsiMethod method) {
- return getFromMapMappingParameter( method ) != null;
- }
-
@Nullable
- public static PsiParameter getFromMapMappingParameter(@NotNull PsiMethod method) {
- PsiParameter[] sourceParameters = getSourceParameters( method );
- if (sourceParameters.length == 1) {
- PsiParameter parameter = sourceParameters[0];
- if (parameter != null && PsiType.getTypeByName( "java.util.Map", method.getProject(),
- method.getResolveScope() ).isAssignableFrom( parameter.getType() ) ) {
- return parameter;
- }
+ public static PsiType[] getGenericTypes(@Nullable PsiParameter fromMapMappingParameter) {
+ if (fromMapMappingParameter == null ||
+ !(fromMapMappingParameter.getType() instanceof PsiClassReferenceType)) {
+ return null;
}
- return null;
+ return ((PsiClassReferenceType) fromMapMappingParameter.getType()).getParameters();
}
}
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index 62863a55..c3b0b96f 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -115,7 +115,7 @@
allQuickFixes = myFixture.getAllQuickFixes();
assertThat( allQuickFixes ).isEmpty();
}
+
+ public void testFromMapMappingMapTypeWithSourceParameterNoInspection() {
+ doTest();
+ List allQuickFixes = myFixture.getAllQuickFixes();
+ assertThat( allQuickFixes ).isEmpty();
+ }
}
diff --git a/src/test/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesWithFromMapMappingTest.java b/src/test/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesWithFromMapMappingTest.java
index 54bbd6df..07df7817 100644
--- a/src/test/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesWithFromMapMappingTest.java
+++ b/src/test/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesWithFromMapMappingTest.java
@@ -40,4 +40,39 @@ public void testUnmappedTargetPropertiesWithFromMapMapping() {
List allQuickFixes = myFixture.getAllQuickFixes();
assertThat( allQuickFixes ).isEmpty();
}
+
+ public void testUnmappedTargetPropertiesWithFromMapMappingMultiSource() {
+ doTest();
+ List allQuickFixes = myFixture.getAllQuickFixes();
+ assertThat( allQuickFixes ).isEmpty();
+ }
+
+ public void testUnmappedTargetPropertiesWithFromMapMappingMultiSourceWrongKeyType() {
+ doTest();
+ List allQuickFixes = myFixture.getAllQuickFixes();
+ assertThat( allQuickFixes )
+ .extracting( IntentionAction::getText )
+ .as( "Intent Text" )
+ .containsExactly(
+ "Ignore unmapped target property: 'matching'",
+ "Add unmapped target property: 'matching'",
+
+ "Ignore unmapped target property: 'moreTarget'",
+ "Add unmapped target property: 'moreTarget'",
+
+ "Ignore unmapped target property: 'testName'",
+ "Add unmapped target property: 'testName'",
+ "Ignore all unmapped target properties",
+
+ "Ignore unmapped target property: 'matching'",
+ "Add unmapped target property: 'matching'",
+
+ "Ignore unmapped target property: 'moreTarget'",
+ "Add unmapped target property: 'moreTarget'",
+
+ "Ignore unmapped target property: 'testName'",
+ "Add unmapped target property: 'testName'",
+ "Ignore all unmapped target properties"
+ );
+ }
}
diff --git a/testData/inspection/FromMapMappingMapTypeInspectionRawMap.java b/testData/inspection/FromMapMappingMapTypeInspectionRawMap.java
index 2ee2e297..00d1ef04 100644
--- a/testData/inspection/FromMapMappingMapTypeInspectionRawMap.java
+++ b/testData/inspection/FromMapMappingMapTypeInspectionRawMap.java
@@ -34,9 +34,9 @@ interface NotMapStructMapper {
@Mapper
interface NoMappingMapper {
- Target map(Map source);
+ Target map(Map source);
- Target map(HashMap source);
+ Target map(HashMap source);
}
@Mapper
@@ -48,9 +48,9 @@ interface MultiSourceMappingsMapper {
@Mapper
interface UpdateMapper {
- void update(@MappingTarget Target target, Map source);
+ void update(@MappingTarget Target target, Map source);
- void update(@MappingTarget Target target, HashMap source);
+ void update(@MappingTarget Target target, HashMap source);
}
@Mapper
diff --git a/testData/inspection/FromMapMappingMapTypeInspectionWrongKeyType.java b/testData/inspection/FromMapMappingMapTypeInspectionWrongKeyType.java
index 9e7a082f..3cfd9ada 100644
--- a/testData/inspection/FromMapMappingMapTypeInspectionWrongKeyType.java
+++ b/testData/inspection/FromMapMappingMapTypeInspectionWrongKeyType.java
@@ -34,9 +34,9 @@ interface NotMapStructMapper {
@Mapper
interface NoMappingMapper {
- Target map(Map source);
+ Target map(Map source);
- Target map(HashMap source);
+ Target map(HashMap source);
}
@Mapper
@@ -48,9 +48,9 @@ interface MultiSourceMappingsMapper {
@Mapper
interface UpdateMapper {
- void update(@MappingTarget Target target, Map source);
+ void update(@MappingTarget Target target, Map source);
- void update(@MappingTarget Target target, HashMap source);
+ void update(@MappingTarget Target target, HashMap source);
}
@Mapper
diff --git a/testData/inspection/FromMapMappingMapTypeWithSourceParameterNoInspection.java b/testData/inspection/FromMapMappingMapTypeWithSourceParameterNoInspection.java
new file mode 100644
index 00000000..2c941056
--- /dev/null
+++ b/testData/inspection/FromMapMappingMapTypeWithSourceParameterNoInspection.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Context;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+
+import java.util.HashMap;
+import java.util.Map;
+
+class Target {
+
+ private Map innerMap;
+
+ public Target(Map innerMap) {
+ this.innerMap = innerMap;
+ }
+}
+
+@Mapper
+interface ParameterNameMapper {
+
+ Target map(Map innerMap);
+
+ Target map(HashMap innerMap);
+
+ void update(@MappingTarget Target target, Map innerMap);
+
+ void update(@MappingTarget Target target, HashMap innerMap);
+}
+
+@Mapper
+interface MappingSourceMapper {
+
+ @Mapping(source = "source", target = "innerMap")
+ Target map(Map source);
+
+ @Mapping(source = "source", target = "innerMap")
+ Target map(HashMap source);
+
+ @Mapping(source = "source", target = "innerMap")
+ void update(@MappingTarget Target target, Map source);
+
+ @Mapping(source = "source", target = "innerMap")
+ void update(@MappingTarget Target target, HashMap source);
+}
+
+@Mapper
+interface UpdateMapper {
+
+
+
+}
\ No newline at end of file
diff --git a/testData/inspection/UnmappedTargetPropertiesWithFromMapMapping.java b/testData/inspection/UnmappedTargetPropertiesWithFromMapMapping.java
index eb2dc551..18fcead5 100644
--- a/testData/inspection/UnmappedTargetPropertiesWithFromMapMapping.java
+++ b/testData/inspection/UnmappedTargetPropertiesWithFromMapMapping.java
@@ -16,10 +16,10 @@
@Mapper
interface SingleMapper {
- Target map(Map source);
+ Target map(Map source, String secondSource);
}
@Mapper
abstract class AbstractMapperWitAbstractMethod {
- abstract Target map(Map source);
+ abstract Target map(Map source, String secondSource);
}
diff --git a/testData/inspection/UnmappedTargetPropertiesWithFromMapMappingMultiSource.java b/testData/inspection/UnmappedTargetPropertiesWithFromMapMappingMultiSource.java
new file mode 100644
index 00000000..eb2dc551
--- /dev/null
+++ b/testData/inspection/UnmappedTargetPropertiesWithFromMapMappingMultiSource.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Context;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.Mappings;
+import org.example.data.UnmappedTargetPropertiesData.Target;
+
+import java.util.Map;
+
+@Mapper
+interface SingleMapper {
+
+ Target map(Map source);
+}
+@Mapper
+abstract class AbstractMapperWitAbstractMethod {
+
+ abstract Target map(Map source);
+}
diff --git a/testData/inspection/UnmappedTargetPropertiesWithFromMapMappingMultiSourceWrongKeyType.java b/testData/inspection/UnmappedTargetPropertiesWithFromMapMappingMultiSourceWrongKeyType.java
new file mode 100644
index 00000000..9396e4af
--- /dev/null
+++ b/testData/inspection/UnmappedTargetPropertiesWithFromMapMappingMultiSourceWrongKeyType.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright MapStruct Authors.
+ *
+ * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
+ */
+
+import org.mapstruct.Context;
+import org.mapstruct.Mapper;
+import org.mapstruct.Mapping;
+import org.mapstruct.MappingTarget;
+import org.mapstruct.Mappings;
+import org.example.data.UnmappedTargetPropertiesData.Target;
+
+import java.util.Map;
+
+@Mapper
+interface SingleMapper {
+
+ Target map(Map source);
+}
+@Mapper
+abstract class AbstractMapperWitAbstractMethod {
+
+ abstract Target map(Map source);
+}