Skip to content

Commit

Permalink
Support configuring order of source and target in @Mapping for Add …
Browse files Browse the repository at this point in the history
…unmapped property quick fix

Fixes #51
  • Loading branch information
filiphr committed Apr 3, 2021
1 parent a7c5477 commit 100a425
Show file tree
Hide file tree
Showing 10 changed files with 325 additions and 6 deletions.
4 changes: 4 additions & 0 deletions change-notes.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
<html>
<h2>1.3.0</h2>
<ul>
<li>Quick Fix: support for configuring the order of source and target in <code>@Mapping</code> for "Add unmapped property" fix</li>
</ul>
<h2>1.2.4</h2>
<ul>
<li>Add plugin icon</li>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mapstruct.intellij.MapStructBundle;
import org.mapstruct.intellij.settings.ProjectSettings;
import org.mapstruct.intellij.util.MapStructVersion;
import org.mapstruct.intellij.util.MapstructUtil;
import org.mapstruct.intellij.util.TargetUtils;
Expand Down Expand Up @@ -241,6 +242,25 @@ public void invoke(@NotNull Project project,

}

private static class UnmappedTargetPropertyFixAnnotationSupplier implements Supplier<Collection<PsiAnnotation>> {
private final PsiMethod method;
private final String target;

private UnmappedTargetPropertyFixAnnotationSupplier(PsiMethod method, String target) {
this.method = method;
this.target = target;
}

@Override
public Collection<PsiAnnotation> get() {
String annotationText = ProjectSettings.isPreferSourceBeforeTargetInMapping( method.getProject() ) ?
"@" + MapstructUtil.MAPPING_ANNOTATION_FQN + "(source = \"\", target = \"" + target + "\")" :
"@" + MapstructUtil.MAPPING_ANNOTATION_FQN + "(target = \"" + target + "\", source = \"\")";
return Collections.singleton( JavaPsiFacade.getElementFactory( method.getProject() )
.createAnnotationFromText( annotationText, null ) );
}
}

/**
* Add unmapped property fix. Property fix that adds a {@link org.mapstruct.Mapping} annotation with the
* given {@code target}
Expand All @@ -251,17 +271,12 @@ public void invoke(@NotNull Project project,
* @return the Local Quick fix
*/
private static UnmappedTargetPropertyFix createAddUnmappedTargetPropertyFix(PsiMethod method, String target) {
String fqn = MapstructUtil.MAPPING_ANNOTATION_FQN;
Supplier<Collection<PsiAnnotation>> annotationSupplier =
() -> Collections.singleton( JavaPsiFacade.getElementFactory(
method.getProject() )
.createAnnotationFromText( "@" + fqn + "(target = \"" + target + "\", source=\"\")", null ) );
String message = MapStructBundle.message( "inspection.add.unmapped.target.property", target );
return new UnmappedTargetPropertyFix(
method,
message,
MapStructBundle.message( "intention.add.unmapped.target.property" ),
annotationSupplier
new UnmappedTargetPropertyFixAnnotationSupplier( method, target )
);
}

Expand Down
30 changes: 30 additions & 0 deletions src/main/java/org/mapstruct/intellij/settings/ProjectSettings.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.intellij.settings;

import com.intellij.ide.util.PropertiesComponent;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;

/**
* @author Filip Hrisafov
*/
public interface ProjectSettings {

String PREFIX = "MapStructPlugin";

String PREFER_SOURCE_BEFORE_TARGET_IN_MAPPING =
PREFIX + "PREFER_SOURCE_BEFORE_TARGET_IN_MAPPING";

static boolean isPreferSourceBeforeTargetInMapping(@NotNull Project project) {
return PropertiesComponent.getInstance( project ).getBoolean( PREFER_SOURCE_BEFORE_TARGET_IN_MAPPING, false );
}

static void setPreferSourceBeforeTargetInMapping(@NotNull Project project, boolean value) {
PropertiesComponent.getInstance( project )
.setValue( PREFER_SOURCE_BEFORE_TARGET_IN_MAPPING, String.valueOf( value ), "false" );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.intellij.settings;

import java.awt.BorderLayout;
import javax.swing.JComponent;
import javax.swing.JPanel;

import com.intellij.ui.IdeBorderFactory;
import com.intellij.ui.components.JBCheckBox;
import com.intellij.util.ui.FormBuilder;
import org.mapstruct.intellij.MapStructBundle;

/**
* @author Filip Hrisafov
*/
public class ProjectSettingsComponent {

private final JPanel mainPanel;
private final JBCheckBox preferSourceBeforeTargetInMapping;

public ProjectSettingsComponent() {
this.preferSourceBeforeTargetInMapping = new JBCheckBox( MapStructBundle.message(
"plugin.settings.quickFix.preferSourceBeforeTargetInMapping" ), false );
JPanel quickFixProperties = new JPanel( new BorderLayout() );
quickFixProperties.setBorder( IdeBorderFactory.createTitledBorder( MapStructBundle.message(
"plugin.settings.quickFix.title" ), false ) );

quickFixProperties.add( this.preferSourceBeforeTargetInMapping, BorderLayout.NORTH );
this.mainPanel = FormBuilder.createFormBuilder()
.addComponent( quickFixProperties )
.addComponentFillVertically( new JPanel(), 0 )
.getPanel();
}

public JPanel getPanel() {
return mainPanel;
}

public JComponent getPreferredFocusedComponent() {
return preferSourceBeforeTargetInMapping;
}

public boolean getPreferSourceBeforeTargetInMapping() {
return preferSourceBeforeTargetInMapping.isSelected();
}

public void setPreferSourceBeforeTargetInMapping(boolean newState) {
preferSourceBeforeTargetInMapping.setSelected( newState );
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.intellij.settings;

import javax.swing.JComponent;

import com.intellij.openapi.options.Configurable;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.Nls;
import org.mapstruct.intellij.MapStructBundle;

import static org.mapstruct.intellij.settings.ProjectSettings.isPreferSourceBeforeTargetInMapping;

/**
* @author Filip Hrisafov
*/
public class ProjectSettingsPage implements Configurable {

private ProjectSettingsComponent settingsComponent;

private final Project myProject;

public ProjectSettingsPage(Project project) {
myProject = project;
}

@Nls
@Override
public String getDisplayName() {
return MapStructBundle.message( "plugin.settings.title" );
}

@Override
public JComponent createComponent() {
settingsComponent = new ProjectSettingsComponent();
return settingsComponent.getPanel();
}

@Override
public JComponent getPreferredFocusedComponent() {
return settingsComponent.getPreferredFocusedComponent();
}

@Override
public boolean isModified() {
boolean modified = settingsComponent.getPreferSourceBeforeTargetInMapping() !=
isPreferSourceBeforeTargetInMapping( myProject );
return modified;
}

@Override
public void apply() {
ProjectSettings.setPreferSourceBeforeTargetInMapping(
myProject,
settingsComponent.getPreferSourceBeforeTargetInMapping()
);
}

@Override
public void reset() {
settingsComponent.setPreferSourceBeforeTargetInMapping( isPreferSourceBeforeTargetInMapping( myProject ) );
}

@Override
public void disposeUIResources() {
settingsComponent = null;
}
}
6 changes: 6 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
<renameHandler implementation="org.mapstruct.intellij.rename.MapstructSourceTargetParameterRenameHandler"/>
<multiHostInjector implementation="org.mapstruct.intellij.expression.JavaExpressionInjector"/>

<projectConfigurable groupId="language"
id="preferences.language.MapStruct"
bundle="org.mapstruct.intellij.messages.MapStructBundle"
key="plugin.settings.title"
instance="org.mapstruct.intellij.settings.ProjectSettingsPage"/>

<localInspection language="JAVA"
enabledByDefault="true"
level="ERROR"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ inspection.wrong.usage.mappers.factory.remove.mappers.usage=Remove usage of Mapp
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
plugin.settings.title=MapStruct
plugin.settings.quickFix.title=Quick fix properties
plugin.settings.quickFix.preferSourceBeforeTargetInMapping=Prefer source before target in @Mapping
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
*/
package org.mapstruct.intellij.inspection;

import java.util.List;

import com.intellij.codeInsight.intention.IntentionAction;
import org.jetbrains.annotations.NotNull;
import org.mapstruct.intellij.settings.ProjectSettings;

import static org.assertj.core.api.Assertions.assertThat;

/**
* @author Filip Hrisafov
*/
public class UnmappedTargetPropertiesInspectionSourceBeforeTargetTest extends BaseInspectionTest {

@NotNull
@Override
protected Class<UnmappedTargetPropertiesInspection> getInspection() {
return UnmappedTargetPropertiesInspection.class;
}

@Override
protected void setUp() throws Exception {
super.setUp();
myFixture.copyFileToProject(
"UnmappedTargetPropertiesData.java",
"org/example/data/UnmappedTargetPropertiesData.java"
);
}

public void testUnmappedTargetPropertiesSourceBeforeTarget() {
ProjectSettings.setPreferSourceBeforeTargetInMapping( myFixture.getProject(), true );
doTest();
String testName = getTestName( false );
List<IntentionAction> allQuickFixes = myFixture.getAllQuickFixes();

assertThat( allQuickFixes )
.extracting( IntentionAction::getText )
.as( "Intent Text" )
.containsExactly(
"Ignore unmapped target property: 'testName'",
"Add unmapped target property: 'testName'",
"Ignore unmapped target property: 'moreTarget'",
"Add unmapped target property: 'moreTarget'",
"Ignore unmapped target property: 'testName'",
"Add unmapped target property: 'testName'"
);

allQuickFixes.forEach( myFixture::launchAction );
myFixture.checkResultByFile( testName + "_after.java" );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://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 org.example.data.UnmappedTargetPropertiesData.Source;

@Mapper
interface SingleMappingsMapper {

@Mappings({
@Mapping(target = "moreTarget", source = "moreSource")
})
Target <warning descr="Unmapped target property: testName">map</warning>(Source source);
}

@Mapper
interface SingleMappingMapper {

@Mapping(target = "testName", source = "name")
Target <warning descr="Unmapped target property: moreTarget">map</warning>(Source source);
}

@Mapper
interface UpdateMapper {

@Mapping(target = "moreTarget", source = "moreSource")
void <warning descr="Unmapped target property: testName">update</warning>(@MappingTarget Target target, Source source);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at http://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 org.example.data.UnmappedTargetPropertiesData.Source;

@Mapper
interface SingleMappingsMapper {

@Mappings({
@Mapping(target = "moreTarget", source = "moreSource"),
@Mapping(target = "testName", ignore = true),
@Mapping(source = "", target = "testName")
})
Target map(Source source);
}

@Mapper
interface SingleMappingMapper {

@Mapping(source = "", target = "moreTarget")
@Mapping(target = "moreTarget", ignore = true)
@Mapping(target = "testName", source = "name")
Target map(Source source);
}

@Mapper
interface UpdateMapper {

@Mapping(source = "", target = "testName")
@Mapping(target = "testName", ignore = true)
@Mapping(target = "moreTarget", source = "moreSource")
void update(@MappingTarget Target target, Source source);
}

0 comments on commit 100a425

Please sign in to comment.