Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions src/main/java/org/mapstruct/intellij/util/MapstructUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ private MapstructUtil() {
}

public static LookupElement[] asLookup(Map<String, Pair<? extends PsiElement, PsiSubstitutor>> accessors,
Function<PsiElement, PsiType> typeMapper) {
Function<PsiElement, PsiType> typeMapper) {
if ( !accessors.isEmpty() ) {
LookupElement[] lookupElements = new LookupElement[accessors.size()];
int index = 0;
Expand Down Expand Up @@ -163,7 +163,7 @@ public static <T extends PsiElement> LookupElement asLookup(String propertyName,
}

public static LookupElement asLookup(String propertyName, @NotNull Pair<? extends PsiElement, PsiSubstitutor> pair,
Function<PsiElement, PsiType> typeMapper, Icon icon) {
Function<PsiElement, PsiType> typeMapper, Icon icon) {
PsiElement member = pair.getFirst();
PsiSubstitutor substitutor = pair.getSecond();

Expand Down Expand Up @@ -200,17 +200,35 @@ private static boolean isPublic(@NotNull PsiField field) {

public static boolean isPublicModifiable(@NotNull PsiField field) {
return isPublicNonStatic( field ) &&
!field.hasModifierProperty( PsiModifier.FINAL );
!field.hasModifierProperty( PsiModifier.FINAL );
}

public static boolean isFluentSetter(@NotNull PsiMethod method, PsiType psiType) {
return !psiType.getCanonicalText().startsWith( "java.lang" ) &&
method.getReturnType() != null &&
!isAdderWithUpperCase4thCharacter( method ) &&
TypeConversionUtil.isAssignable(
psiType,
PsiUtil.resolveGenericsClassInType( psiType ).getSubstitutor().substitute( method.getReturnType() )
);
isAssignableFromReturnTypeOrSuperTypes( psiType, method.getReturnType() );
}

private static boolean isAssignableFromReturnTypeOrSuperTypes(PsiType psiType, PsiType returnType) {

if ( isAssignableFrom( psiType, returnType ) ) {
return true;
}

for ( PsiType superType : returnType.getSuperTypes() ) {
if ( isAssignableFrom( psiType, superType ) ) {
return true;
}
}
return false;
}

private static boolean isAssignableFrom(PsiType psiType, @Nullable PsiType returnType) {
return TypeConversionUtil.isAssignable(
psiType,
PsiUtil.resolveGenericsClassInType( psiType ).getSubstitutor().substitute( returnType )
);
}

private static boolean isAdderWithUpperCase4thCharacter(@NotNull PsiMethod method) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* 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 java.util.List;

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

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

/**
* @author Oliver Erhart
*/
public class UnmappedSuperBuilderTargetPropertiesInspectionTest extends BaseInspectionTest {

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

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

public void testUnmappedSuperBuilderTargetProperties() {
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: '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: 'testName'",
"Add unmapped target property: 'testName'",
"Ignore unmapped target property: 'moreTarget'",
"Add unmapped target property: 'moreTarget'"
);

allQuickFixes.forEach( myFixture::launchAction );
myFixture.checkResultByFile( testName + "_after.java" );
}
}
65 changes: 65 additions & 0 deletions testData/inspection/UnmappedSuperBuilderTargetProperties.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* 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.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.example.data.UnmappedSuperBuilderTargetPropertiesData.Target;
import org.example.data.UnmappedSuperBuilderTargetPropertiesData.Source;

interface NotMapStructMapper {

Target map(Source 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 NoMappingMapper {

Target <warning descr="Unmapped target properties: moreTarget, testName">map</warning>(Source source);

@InheritInverseConfiguration
Source reverse(Target target);
}

@Mapper
interface AllMappingMapper {

@Mapping(target = "testName", source = "name")
@Mapping(target = "moreTarget", source = "moreSource")
Target mapWithAllMapping(Source source);
}

@Mapper
interface UpdateMapper {

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

@Mapper
interface MultiSourceUpdateMapper {

void <warning descr="Unmapped target property: moreTarget">update</warning>(@MappingTarget Target moreTarget, Source source, String testName, @Context String matching);
}
128 changes: 128 additions & 0 deletions testData/inspection/UnmappedSuperBuilderTargetPropertiesData.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Copyright MapStruct Authors.
*
* Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
*/
package org.example.data;

public class UnmappedSuperBuilderTargetPropertiesData {
public static class Source {

private String name;
private String matching;
private String moreSource;
private String onlyInSource;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public String getMatching() {
return matching;
}

public void setMatching(String matching) {
this.matching = matching;
}

public String getMoreSource() {
return moreSource;
}

public void setMoreSource(String moreSource) {
this.moreSource = moreSource;
}

public String getOnlyInSource() {
return onlyInSource;
}

public void setOnlyInSource(String onlyInSource) {
this.onlyInSource = onlyInSource;
}
}

public static class Target {

private String testName;
private String matching;
private String moreTarget;

protected Target(TargetBuilder<?, ?> b) {
this.testName = b.testName;
this.matching = b.matching;
this.moreTarget = b.moreTarget;
}

public static TargetBuilder<?, ?> builder() {
return new TargetBuilderImpl();
}

public String getTestName() {
return this.testName;
}

public String getMatching() {
return this.matching;
}

public String getMoreTarget() {
return this.moreTarget;
}

public void setTestName(String testName) {
this.testName = testName;
}

public void setMatching(String matching) {
this.matching = matching;
}

public void setMoreTarget(String moreTarget) {
this.moreTarget = moreTarget;
}

public static abstract class TargetBuilder<C extends Target, B extends TargetBuilder<C, B>> {
private String testName;
private String matching;
private String moreTarget;

public B testName(String testName) {
this.testName = testName;
return self();
}

public B matching(String matching) {
this.matching = matching;
return self();
}

public B moreTarget(String moreTarget) {
this.moreTarget = moreTarget;
return self();
}

protected abstract B self();

public abstract C build();
}

private static final class TargetBuilderImpl extends TargetBuilder<Target, TargetBuilderImpl> {
private TargetBuilderImpl() {
}

protected TargetBuilderImpl self() {
return this;
}

public Target build() {
return new Target( this );
}
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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.InheritInverseConfiguration;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.MappingTarget;
import org.mapstruct.Mappings;
import org.example.data.UnmappedSuperBuilderTargetPropertiesData.Target;
import org.example.data.UnmappedSuperBuilderTargetPropertiesData.Source;

interface NotMapStructMapper {

Target map(Source source);
}

@Mapper
interface SingleMappingsMapper {

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

@Mapper
interface SingleMappingMapper {

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

@Mapper
interface NoMappingMapper {

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

@InheritInverseConfiguration
Source reverse(Target target);
}

@Mapper
interface AllMappingMapper {

@Mapping(target = "testName", source = "name")
@Mapping(target = "moreTarget", source = "moreSource")
Target mapWithAllMapping(Source source);
}

@Mapper
interface UpdateMapper {

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

@Mapper
interface MultiSourceUpdateMapper {

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