Skip to content

Commit 100a425

Browse files
committed
Support configuring order of source and target in @Mapping for Add unmapped property quick fix
Fixes #51
1 parent a7c5477 commit 100a425

File tree

10 files changed

+325
-6
lines changed

10 files changed

+325
-6
lines changed

change-notes.html

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
<html>
2+
<h2>1.3.0</h2>
3+
<ul>
4+
<li>Quick Fix: support for configuring the order of source and target in <code>@Mapping</code> for "Add unmapped property" fix</li>
5+
</ul>
26
<h2>1.2.4</h2>
37
<ul>
48
<li>Add plugin icon</li>

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

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.jetbrains.annotations.NotNull;
3434
import org.jetbrains.annotations.Nullable;
3535
import org.mapstruct.intellij.MapStructBundle;
36+
import org.mapstruct.intellij.settings.ProjectSettings;
3637
import org.mapstruct.intellij.util.MapStructVersion;
3738
import org.mapstruct.intellij.util.MapstructUtil;
3839
import org.mapstruct.intellij.util.TargetUtils;
@@ -241,6 +242,25 @@ public void invoke(@NotNull Project project,
241242

242243
}
243244

245+
private static class UnmappedTargetPropertyFixAnnotationSupplier implements Supplier<Collection<PsiAnnotation>> {
246+
private final PsiMethod method;
247+
private final String target;
248+
249+
private UnmappedTargetPropertyFixAnnotationSupplier(PsiMethod method, String target) {
250+
this.method = method;
251+
this.target = target;
252+
}
253+
254+
@Override
255+
public Collection<PsiAnnotation> get() {
256+
String annotationText = ProjectSettings.isPreferSourceBeforeTargetInMapping( method.getProject() ) ?
257+
"@" + MapstructUtil.MAPPING_ANNOTATION_FQN + "(source = \"\", target = \"" + target + "\")" :
258+
"@" + MapstructUtil.MAPPING_ANNOTATION_FQN + "(target = \"" + target + "\", source = \"\")";
259+
return Collections.singleton( JavaPsiFacade.getElementFactory( method.getProject() )
260+
.createAnnotationFromText( annotationText, null ) );
261+
}
262+
}
263+
244264
/**
245265
* Add unmapped property fix. Property fix that adds a {@link org.mapstruct.Mapping} annotation with the
246266
* given {@code target}
@@ -251,17 +271,12 @@ public void invoke(@NotNull Project project,
251271
* @return the Local Quick fix
252272
*/
253273
private static UnmappedTargetPropertyFix createAddUnmappedTargetPropertyFix(PsiMethod method, String target) {
254-
String fqn = MapstructUtil.MAPPING_ANNOTATION_FQN;
255-
Supplier<Collection<PsiAnnotation>> annotationSupplier =
256-
() -> Collections.singleton( JavaPsiFacade.getElementFactory(
257-
method.getProject() )
258-
.createAnnotationFromText( "@" + fqn + "(target = \"" + target + "\", source=\"\")", null ) );
259274
String message = MapStructBundle.message( "inspection.add.unmapped.target.property", target );
260275
return new UnmappedTargetPropertyFix(
261276
method,
262277
message,
263278
MapStructBundle.message( "intention.add.unmapped.target.property" ),
264-
annotationSupplier
279+
new UnmappedTargetPropertyFixAnnotationSupplier( method, target )
265280
);
266281
}
267282

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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.settings;
7+
8+
import com.intellij.ide.util.PropertiesComponent;
9+
import com.intellij.openapi.project.Project;
10+
import org.jetbrains.annotations.NotNull;
11+
12+
/**
13+
* @author Filip Hrisafov
14+
*/
15+
public interface ProjectSettings {
16+
17+
String PREFIX = "MapStructPlugin";
18+
19+
String PREFER_SOURCE_BEFORE_TARGET_IN_MAPPING =
20+
PREFIX + "PREFER_SOURCE_BEFORE_TARGET_IN_MAPPING";
21+
22+
static boolean isPreferSourceBeforeTargetInMapping(@NotNull Project project) {
23+
return PropertiesComponent.getInstance( project ).getBoolean( PREFER_SOURCE_BEFORE_TARGET_IN_MAPPING, false );
24+
}
25+
26+
static void setPreferSourceBeforeTargetInMapping(@NotNull Project project, boolean value) {
27+
PropertiesComponent.getInstance( project )
28+
.setValue( PREFER_SOURCE_BEFORE_TARGET_IN_MAPPING, String.valueOf( value ), "false" );
29+
}
30+
}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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.settings;
7+
8+
import java.awt.BorderLayout;
9+
import javax.swing.JComponent;
10+
import javax.swing.JPanel;
11+
12+
import com.intellij.ui.IdeBorderFactory;
13+
import com.intellij.ui.components.JBCheckBox;
14+
import com.intellij.util.ui.FormBuilder;
15+
import org.mapstruct.intellij.MapStructBundle;
16+
17+
/**
18+
* @author Filip Hrisafov
19+
*/
20+
public class ProjectSettingsComponent {
21+
22+
private final JPanel mainPanel;
23+
private final JBCheckBox preferSourceBeforeTargetInMapping;
24+
25+
public ProjectSettingsComponent() {
26+
this.preferSourceBeforeTargetInMapping = new JBCheckBox( MapStructBundle.message(
27+
"plugin.settings.quickFix.preferSourceBeforeTargetInMapping" ), false );
28+
JPanel quickFixProperties = new JPanel( new BorderLayout() );
29+
quickFixProperties.setBorder( IdeBorderFactory.createTitledBorder( MapStructBundle.message(
30+
"plugin.settings.quickFix.title" ), false ) );
31+
32+
quickFixProperties.add( this.preferSourceBeforeTargetInMapping, BorderLayout.NORTH );
33+
this.mainPanel = FormBuilder.createFormBuilder()
34+
.addComponent( quickFixProperties )
35+
.addComponentFillVertically( new JPanel(), 0 )
36+
.getPanel();
37+
}
38+
39+
public JPanel getPanel() {
40+
return mainPanel;
41+
}
42+
43+
public JComponent getPreferredFocusedComponent() {
44+
return preferSourceBeforeTargetInMapping;
45+
}
46+
47+
public boolean getPreferSourceBeforeTargetInMapping() {
48+
return preferSourceBeforeTargetInMapping.isSelected();
49+
}
50+
51+
public void setPreferSourceBeforeTargetInMapping(boolean newState) {
52+
preferSourceBeforeTargetInMapping.setSelected( newState );
53+
}
54+
55+
}
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.intellij.settings;
7+
8+
import javax.swing.JComponent;
9+
10+
import com.intellij.openapi.options.Configurable;
11+
import com.intellij.openapi.project.Project;
12+
import org.jetbrains.annotations.Nls;
13+
import org.mapstruct.intellij.MapStructBundle;
14+
15+
import static org.mapstruct.intellij.settings.ProjectSettings.isPreferSourceBeforeTargetInMapping;
16+
17+
/**
18+
* @author Filip Hrisafov
19+
*/
20+
public class ProjectSettingsPage implements Configurable {
21+
22+
private ProjectSettingsComponent settingsComponent;
23+
24+
private final Project myProject;
25+
26+
public ProjectSettingsPage(Project project) {
27+
myProject = project;
28+
}
29+
30+
@Nls
31+
@Override
32+
public String getDisplayName() {
33+
return MapStructBundle.message( "plugin.settings.title" );
34+
}
35+
36+
@Override
37+
public JComponent createComponent() {
38+
settingsComponent = new ProjectSettingsComponent();
39+
return settingsComponent.getPanel();
40+
}
41+
42+
@Override
43+
public JComponent getPreferredFocusedComponent() {
44+
return settingsComponent.getPreferredFocusedComponent();
45+
}
46+
47+
@Override
48+
public boolean isModified() {
49+
boolean modified = settingsComponent.getPreferSourceBeforeTargetInMapping() !=
50+
isPreferSourceBeforeTargetInMapping( myProject );
51+
return modified;
52+
}
53+
54+
@Override
55+
public void apply() {
56+
ProjectSettings.setPreferSourceBeforeTargetInMapping(
57+
myProject,
58+
settingsComponent.getPreferSourceBeforeTargetInMapping()
59+
);
60+
}
61+
62+
@Override
63+
public void reset() {
64+
settingsComponent.setPreferSourceBeforeTargetInMapping( isPreferSourceBeforeTargetInMapping( myProject ) );
65+
}
66+
67+
@Override
68+
public void disposeUIResources() {
69+
settingsComponent = null;
70+
}
71+
}

src/main/resources/META-INF/plugin.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@
4040
<renameHandler implementation="org.mapstruct.intellij.rename.MapstructSourceTargetParameterRenameHandler"/>
4141
<multiHostInjector implementation="org.mapstruct.intellij.expression.JavaExpressionInjector"/>
4242

43+
<projectConfigurable groupId="language"
44+
id="preferences.language.MapStruct"
45+
bundle="org.mapstruct.intellij.messages.MapStructBundle"
46+
key="plugin.settings.title"
47+
instance="org.mapstruct.intellij.settings.ProjectSettingsPage"/>
48+
4349
<localInspection language="JAVA"
4450
enabledByDefault="true"
4551
level="ERROR"

src/main/resources/org/mapstruct/intellij/messages/MapStructBundle.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,3 +14,6 @@ inspection.wrong.usage.mappers.factory.remove.mappers.usage=Remove usage of Mapp
1414
intention.add.ignore.all.unmapped.target.properties=Add ignore all unmapped target properties
1515
intention.add.ignore.unmapped.target.property=Add ignore unmapped target property
1616
intention.add.unmapped.target.property=Add unmapped target property
17+
plugin.settings.title=MapStruct
18+
plugin.settings.quickFix.title=Quick fix properties
19+
plugin.settings.quickFix.preferSourceBeforeTargetInMapping=Prefer source before target in @Mapping
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
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+
import org.mapstruct.intellij.settings.ProjectSettings;
13+
14+
import static org.assertj.core.api.Assertions.assertThat;
15+
16+
/**
17+
* @author Filip Hrisafov
18+
*/
19+
public class UnmappedTargetPropertiesInspectionSourceBeforeTargetTest extends BaseInspectionTest {
20+
21+
@NotNull
22+
@Override
23+
protected Class<UnmappedTargetPropertiesInspection> getInspection() {
24+
return UnmappedTargetPropertiesInspection.class;
25+
}
26+
27+
@Override
28+
protected void setUp() throws Exception {
29+
super.setUp();
30+
myFixture.copyFileToProject(
31+
"UnmappedTargetPropertiesData.java",
32+
"org/example/data/UnmappedTargetPropertiesData.java"
33+
);
34+
}
35+
36+
public void testUnmappedTargetPropertiesSourceBeforeTarget() {
37+
ProjectSettings.setPreferSourceBeforeTargetInMapping( myFixture.getProject(), true );
38+
doTest();
39+
String testName = getTestName( false );
40+
List<IntentionAction> allQuickFixes = myFixture.getAllQuickFixes();
41+
42+
assertThat( allQuickFixes )
43+
.extracting( IntentionAction::getText )
44+
.as( "Intent Text" )
45+
.containsExactly(
46+
"Ignore unmapped target property: 'testName'",
47+
"Add unmapped target property: 'testName'",
48+
"Ignore unmapped target property: 'moreTarget'",
49+
"Add unmapped target property: 'moreTarget'",
50+
"Ignore unmapped target property: 'testName'",
51+
"Add unmapped target property: 'testName'"
52+
);
53+
54+
allQuickFixes.forEach( myFixture::launchAction );
55+
myFixture.checkResultByFile( testName + "_after.java" );
56+
}
57+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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.UnmappedTargetPropertiesData.Target;
13+
import org.example.data.UnmappedTargetPropertiesData.Source;
14+
15+
@Mapper
16+
interface SingleMappingsMapper {
17+
18+
@Mappings({
19+
@Mapping(target = "moreTarget", source = "moreSource")
20+
})
21+
Target <warning descr="Unmapped target property: testName">map</warning>(Source source);
22+
}
23+
24+
@Mapper
25+
interface SingleMappingMapper {
26+
27+
@Mapping(target = "testName", source = "name")
28+
Target <warning descr="Unmapped target property: moreTarget">map</warning>(Source source);
29+
}
30+
31+
@Mapper
32+
interface UpdateMapper {
33+
34+
@Mapping(target = "moreTarget", source = "moreSource")
35+
void <warning descr="Unmapped target property: testName">update</warning>(@MappingTarget Target target, Source source);
36+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
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.UnmappedTargetPropertiesData.Target;
13+
import org.example.data.UnmappedTargetPropertiesData.Source;
14+
15+
@Mapper
16+
interface SingleMappingsMapper {
17+
18+
@Mappings({
19+
@Mapping(target = "moreTarget", source = "moreSource"),
20+
@Mapping(target = "testName", ignore = true),
21+
@Mapping(source = "", target = "testName")
22+
})
23+
Target map(Source source);
24+
}
25+
26+
@Mapper
27+
interface SingleMappingMapper {
28+
29+
@Mapping(source = "", target = "moreTarget")
30+
@Mapping(target = "moreTarget", ignore = true)
31+
@Mapping(target = "testName", source = "name")
32+
Target map(Source source);
33+
}
34+
35+
@Mapper
36+
interface UpdateMapper {
37+
38+
@Mapping(source = "", target = "testName")
39+
@Mapping(target = "testName", ignore = true)
40+
@Mapping(target = "moreTarget", source = "moreSource")
41+
void update(@MappingTarget Target target, Source source);
42+
}

0 commit comments

Comments
 (0)