diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructBaseReference.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructBaseReference.java index b2e93288..ee6a0bb9 100644 --- a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructBaseReference.java +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructBaseReference.java @@ -26,7 +26,7 @@ import static org.mapstruct.intellij.util.MapstructUtil.canDescendIntoType; /** - * A base reference to target / source annotation. + * A base reference to annotations holding a reference and possibly nested types. * * @author Filip Hrisafov */ diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructMappingInheritConfigurationReference.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructMappingInheritConfigurationReference.java new file mode 100644 index 00000000..9d2224ef --- /dev/null +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructMappingInheritConfigurationReference.java @@ -0,0 +1,75 @@ +/* + * 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.codeinsight.references; + +import java.util.Objects; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiReference; +import org.jetbrains.annotations.NotNull; +import org.mapstruct.InheritConfiguration; +import org.mapstruct.intellij.util.MapStructVersion; +import org.mapstruct.intellij.util.MapstructUtil; + +import static org.mapstruct.intellij.inspection.inheritance.InheritConfigurationUtils.findInheritConfigurationMethods; + +/** + * Reference for {@link InheritConfiguration#name()}. + * + * @author Oliver Erhart + */ +class MapstructMappingInheritConfigurationReference extends MapstructNonNestedBaseReference { + + private final MapStructVersion mapStructVersion; + + /** + * Create a new {@link MapstructMappingInheritConfigurationReference} with the provided parameters + * + * @param element the element that the reference belongs to + * @param previousReference the previous reference if there is one (in nested properties for example) + * @param rangeInElement the range that the reference represent in the {@code element} + * @param value the matched value (useful when {@code rangeInElement} is empty) + */ + private MapstructMappingInheritConfigurationReference( + PsiElement element, + MapstructMappingInheritConfigurationReference previousReference, + TextRange rangeInElement, String value + ) { + super( element, previousReference, rangeInElement, value ); + mapStructVersion = MapstructUtil.resolveMapStructProjectVersion( element.getContainingFile() + .getOriginalFile() ); + } + + @Override + PsiElement resolveInternal(@NotNull String value, @NotNull PsiMethod mappingMethod) { + + return findInheritConfigurationMethods( mappingMethod, mapStructVersion ) + .filter( a -> Objects.equals( a.getName(), value ) ) + .findAny() + .orElse( null ); + } + + @NotNull + @Override + Object[] getVariantsInternal(@NotNull PsiMethod mappingMethod) { + + return findInheritConfigurationMethods( mappingMethod, mapStructVersion ) + .map( method -> MapstructUtil.asLookup( method, method.getName(), method.getName() ) ) + .filter( Objects::nonNull ) + .toArray(); + } + + /** + * @param psiElement the literal for which references need to be created + * @return the references for the given {@code psiLiteral} + */ + static PsiReference[] create(PsiElement psiElement) { + return MapstructBaseReference.create( psiElement, MapstructMappingInheritConfigurationReference::new, false ); + } + +} diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructMappingInheritInverseConfigurationReference.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructMappingInheritInverseConfigurationReference.java new file mode 100644 index 00000000..264a2f06 --- /dev/null +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructMappingInheritInverseConfigurationReference.java @@ -0,0 +1,81 @@ +/* + * 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.codeinsight.references; + +import java.util.Objects; + +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiReference; +import org.jetbrains.annotations.NotNull; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.intellij.util.MapStructVersion; +import org.mapstruct.intellij.util.MapstructUtil; + +import static org.mapstruct.intellij.inspection.inheritance.InheritConfigurationUtils.findInheritInverseConfigurationMethods; + +/** + * Reference for {@link InheritInverseConfiguration#name()}. + * + * @author Oliver Erhart + */ +class MapstructMappingInheritInverseConfigurationReference extends MapstructNonNestedBaseReference { + + private final MapStructVersion mapStructVersion; + + /** + * Create a new {@link MapstructMappingInheritInverseConfigurationReference} with the provided parameters + * + * @param element the element that the reference belongs to + * @param previousReference the previous reference if there is one (in nested properties for example) + * @param rangeInElement the range that the reference represent in the {@code element} + * @param value the matched value (useful when {@code rangeInElement} is empty) + */ + private MapstructMappingInheritInverseConfigurationReference( + PsiElement element, + MapstructMappingInheritInverseConfigurationReference previousReference, + TextRange rangeInElement, + String value + ) { + + super( element, previousReference, rangeInElement, value ); + mapStructVersion = MapstructUtil.resolveMapStructProjectVersion( element.getContainingFile() + .getOriginalFile() ); + } + + @Override + PsiElement resolveInternal(@NotNull String value, @NotNull PsiMethod mappingMethod) { + + return findInheritInverseConfigurationMethods( mappingMethod, mapStructVersion ) + .filter( a -> Objects.equals( a.getName(), value ) ) + .findAny() + .orElse( null ); + } + + @NotNull + @Override + Object[] getVariantsInternal(@NotNull PsiMethod mappingMethod) { + + return findInheritInverseConfigurationMethods( mappingMethod, mapStructVersion ) + .map( method -> MapstructUtil.asLookup( method, method.getName(), method.getName() ) ) + .filter( Objects::nonNull ) + .toArray(); + } + + /** + * @param psiElement the literal for which references need to be created + * @return the references for the given {@code psiLiteral} + */ + static PsiReference[] create(PsiElement psiElement) { + return MapstructBaseReference.create( + psiElement, + MapstructMappingInheritInverseConfigurationReference::new, + false + ); + } + +} diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructMappingQualifiedByNameReference.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructMappingQualifiedByNameReference.java index 24737950..a17b035f 100644 --- a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructMappingQualifiedByNameReference.java +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructMappingQualifiedByNameReference.java @@ -8,7 +8,6 @@ import java.util.Arrays; import java.util.Objects; import java.util.Optional; -import java.util.stream.Collectors; import java.util.stream.Stream; import com.intellij.codeInsight.lookup.LookupElement; @@ -18,7 +17,6 @@ import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiParameter; import com.intellij.psi.PsiReference; import com.intellij.psi.PsiType; import org.jetbrains.annotations.NotNull; @@ -31,14 +29,13 @@ import static org.mapstruct.intellij.util.MapstructUtil.NAMED_ANNOTATION_FQN; import static org.mapstruct.intellij.util.MapstructUtil.MAPPER_ANNOTATION_FQN; import static org.mapstruct.intellij.util.MapstructUtil.MAPPER_CONFIG_ANNOTATION_FQN; -import static org.mapstruct.intellij.util.MapstructUtil.asLookupWithRepresentableText; /** * Reference for {@link org.mapstruct.Mapping#qualifiedByName()}. * * @author Oliver Erhart */ -class MapstructMappingQualifiedByNameReference extends MapstructBaseReference { +class MapstructMappingQualifiedByNameReference extends MapstructNonNestedBaseReference { /** * Create a new {@link MapstructMappingQualifiedByNameReference} with the provided parameters @@ -54,11 +51,6 @@ private MapstructMappingQualifiedByNameReference(PsiElement element, super( element, previousReference, rangeInElement, value ); } - @Override - PsiElement resolveInternal(@NotNull String value, @NotNull PsiType psiType) { - return null; // not needed - } - @Override PsiElement resolveInternal(@NotNull String value, @NotNull PsiMethod mappingMethod) { @@ -80,12 +72,6 @@ private String getNamedValue(PsiMethod method) { return getStringAttributeValue( namedAnnotation, "value" ); } - @NotNull - @Override - Object[] getVariantsInternal(@NotNull PsiType psiType) { - return LookupElement.EMPTY_ARRAY; // not needed - } - @NotNull @Override Object[] getVariantsInternal(@NotNull PsiMethod mappingMethod) { @@ -139,31 +125,7 @@ private LookupElement methodAsLookup(@NotNull PsiMethod method) { return null; } - return asLookupWithRepresentableText( - method, - lookupString, - lookupString, - String.format( - " %s#%s(%s)", - Objects.requireNonNull( method.getContainingClass() ).getName(), - method.getName(), - formatParameters( method ) - ) - ); - } - - @NotNull - private static String formatParameters(@NotNull PsiMethod method) { - return Arrays.stream( method.getParameterList().getParameters() ) - .map( PsiParameter::getType ) - .map( PsiType::getPresentableText ) - .collect( Collectors.joining( ", " ) ); - } - - @Nullable - @Override - PsiType resolvedType() { - return null; + return MapstructUtil.asLookup( method, lookupString, lookupString ); } /** diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructNonNestedBaseReference.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructNonNestedBaseReference.java new file mode 100644 index 00000000..a66886da --- /dev/null +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructNonNestedBaseReference.java @@ -0,0 +1,51 @@ +/* + * 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.codeinsight.references; + +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiType; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * A base reference to mapstruct annotations without nested types. + * + * @author Oliver Erhart + */ +public abstract class MapstructNonNestedBaseReference extends MapstructBaseReference { + + /** + * Create a reference. + * + * @param element the literal where the text is + * @param previous the previous reference ({@code null} if there is no previous reference) + * @param rangeInElement the range in the {@code element} for which this reference is valid + */ + MapstructNonNestedBaseReference(@NotNull PsiElement element, + @Nullable MapstructBaseReference previous, + TextRange rangeInElement, String value) { + super( element, previous, rangeInElement, value ); + } + + @Override + final PsiElement resolveInternal(@NotNull String value, @NotNull PsiType psiType) { + return null; // not needed + } + + @NotNull + @Override + final Object[] getVariantsInternal(@NotNull PsiType psiType) { + return LookupElement.EMPTY_ARRAY; // not needed + } + + @Override + @Nullable + final PsiType resolvedType() { + return null; // not needed + } +} diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructReferenceContributor.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructReferenceContributor.java index f2edb1fc..81f310c5 100644 --- a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructReferenceContributor.java +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructReferenceContributor.java @@ -10,6 +10,8 @@ import org.jetbrains.annotations.NotNull; import static org.mapstruct.intellij.util.MapstructElementUtils.beanMappingElementPattern; +import static org.mapstruct.intellij.util.MapstructElementUtils.inheritConfigurationElementPattern; +import static org.mapstruct.intellij.util.MapstructElementUtils.inheritInverseConfigurationElementPattern; import static org.mapstruct.intellij.util.MapstructElementUtils.mappingElementPattern; import static org.mapstruct.intellij.util.MapstructElementUtils.valueMappingElementPattern; @@ -48,6 +50,16 @@ public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) valueMappingElementPattern( "target" ), new MappingTargetReferenceProvider( ValueMappingTargetReference::create ) ); + + registrar.registerReferenceProvider( + inheritConfigurationElementPattern( "name" ), + new MappingTargetReferenceProvider( MapstructMappingInheritConfigurationReference::create ) + ); + registrar.registerReferenceProvider( + inheritInverseConfigurationElementPattern( "name" ), + new MappingTargetReferenceProvider( MapstructMappingInheritInverseConfigurationReference::create ) + ); + } } diff --git a/src/main/java/org/mapstruct/intellij/inspection/inheritance/InheritConfigurationUtils.java b/src/main/java/org/mapstruct/intellij/inspection/inheritance/InheritConfigurationUtils.java index 06f8e146..09bdc1e2 100644 --- a/src/main/java/org/mapstruct/intellij/inspection/inheritance/InheritConfigurationUtils.java +++ b/src/main/java/org/mapstruct/intellij/inspection/inheritance/InheritConfigurationUtils.java @@ -10,6 +10,7 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -81,6 +82,65 @@ public static Stream findInheritedTargetProperties(@NotNull PsiMethod ma return ctx.mappedTargets.stream(); } + public static Stream findInheritConfigurationMethods(@NotNull PsiMethod mappingMethod, + MapStructVersion mapStructVersion) { + + Optional inheritContext = findInheritContext( mappingMethod, mapStructVersion ); + + return inheritContext.stream() + .flatMap( ctx -> ctx.forwardTemplateCandidates.stream() ); + } + + public static Stream findInheritInverseConfigurationMethods(@NotNull PsiMethod mappingMethod, + MapStructVersion mapStructVersion) { + + Optional inheritContext = findInheritContext( mappingMethod, mapStructVersion ); + + return inheritContext.stream() + .flatMap( ctx -> ctx.inverseTemplateCandidates.stream() ); + } + + private static Optional findInheritContext(@NotNull PsiMethod mappingMethod, + MapStructVersion mapStructVersion) { + + PsiClass containingClass = mappingMethod.getContainingClass(); + + if ( containingClass == null ) { + return Optional.empty(); + } + + PsiAnnotation containingAnnotation = + Optional.ofNullable( findAnnotation( containingClass, MAPPER_ANNOTATION_FQN ) ) + .orElseGet( () -> findAnnotation( containingClass, MAPPER_CONFIG_ANNOTATION_FQN ) ); + + if ( containingAnnotation == null ) { + return Optional.empty(); + } + + List availableMethods = + findMappingMethodsFromInheritScope( containingClass, containingAnnotation ) + .map( SourceMethod::new ) + .collect( Collectors.toList() ); + List prototypeMethods = + findPrototypeMappingMethods( containingAnnotation ) + .map( SourceMethod::new ) + .collect( Collectors.toList() ); + MappingInheritanceStrategy inheritanceStrategy = + findAnnotatedMappingInheritanceStrategy( containingAnnotation ); + + InheritConfigurationContext ctx = new InheritConfigurationContext( + mapStructVersion, + availableMethods, + prototypeMethods, + new ArrayList<>(), + inheritanceStrategy + ); + + mergeInheritedOptions( new SourceMethod( mappingMethod ), ctx ); + + return Optional.of( ctx ); + } + /** * Merges inherited properties in a recursive way. * @@ -105,6 +165,8 @@ private static void mergeInheritedOptions(@NotNull SourceMethod method, @NotNull targetType, ctx.prototypeMethods ); + applicablePrototypeMethods + .forEach( sourceMethod -> ctx.forwardTemplateCandidates.add( sourceMethod.method ) ); SourceMethod forwardTemplateMethod = getForwardTemplateMethod( @@ -114,15 +176,17 @@ private static void mergeInheritedOptions(@NotNull SourceMethod method, @NotNull ctx ); - Set applicableReversePrototypeMethods = getApplicableReversePrototypeMethods( + Set applicableInversePrototypeMethods = getApplicableInversePrototypeMethods( method, targetType, ctx.prototypeMethods ); + applicableInversePrototypeMethods + .forEach( sourceMethod -> ctx.inverseTemplateCandidates.add( sourceMethod.method ) ); SourceMethod inverseTemplateMethod = getInverseTemplateMethod( - join( ctx.availableMethods, applicableReversePrototypeMethods ), + join( ctx.availableMethods, applicableInversePrototypeMethods ), method, targetType, ctx @@ -155,9 +219,9 @@ private static void mergeInheritedOptions(@NotNull SourceMethod method, @NotNull boolean applyReverse = ctx.inheritanceStrategy == MappingInheritanceStrategy.AUTO_INHERIT_ALL_FROM_CONFIG || ctx.inheritanceStrategy == MappingInheritanceStrategy.AUTO_INHERIT_REVERSE_FROM_CONFIG; if ( inverseTemplateMethod == null && applyReverse ) { - if ( applicableReversePrototypeMethods.size() == 1 ) { + if ( applicableInversePrototypeMethods.size() == 1 ) { findAllDefinedMappingSources( - first( applicableReversePrototypeMethods ).method, + first( applicableInversePrototypeMethods ).method, ctx.mapStructVersion ) .forEach( ctx.mappedTargets::add ); @@ -185,6 +249,7 @@ private static SourceMethod getInverseTemplateMethod( for ( SourceMethod oneMethod : rawMethods ) { if ( mappingMethod.inverses( oneMethod.method, targetType ) ) { candidates.add( oneMethod ); + ctx.inverseTemplateCandidates.add( oneMethod.method ); } } @@ -213,6 +278,7 @@ private static SourceMethod getForwardTemplateMethod( if ( mappingMethod.canInheritFrom( oneMethod.method, targetType ) && !( oneMethod.equals( mappingMethod ) ) ) { candidates.add( oneMethod ); + ctx.forwardTemplateCandidates.add( oneMethod.method ); } } @@ -265,7 +331,7 @@ private static SourceMethod extractInitializedOptions(SourceMethod method, Inher return null; } - private static Set getApplicableReversePrototypeMethods(SourceMethod mappingMethod, + private static Set getApplicableInversePrototypeMethods(SourceMethod mappingMethod, PsiType targetType, List prototypeMethods) { return prototypeMethods.stream() @@ -357,6 +423,8 @@ private static class InheritConfigurationContext { private final List initializingMethods; private final MappingInheritanceStrategy inheritanceStrategy; private final Set mappedTargets = new HashSet<>(); + private final Set forwardTemplateCandidates = new HashSet<>(); + private final Set inverseTemplateCandidates = new HashSet<>(); private InheritConfigurationContext(MapStructVersion mapStructVersion, @NotNull List availableMethods, diff --git a/src/main/java/org/mapstruct/intellij/inspection/inheritance/SourceMethod.java b/src/main/java/org/mapstruct/intellij/inspection/inheritance/SourceMethod.java index 2dbbb647..8faa5f1f 100644 --- a/src/main/java/org/mapstruct/intellij/inspection/inheritance/SourceMethod.java +++ b/src/main/java/org/mapstruct/intellij/inspection/inheritance/SourceMethod.java @@ -137,4 +137,12 @@ public boolean equals(Object o) { public int hashCode() { return Objects.hash( method ); } + + @Override + public String toString() { + return "SourceMethod{" + + "method=" + method + + ", fullyInitialized=" + fullyInitialized + + '}'; + } } diff --git a/src/main/java/org/mapstruct/intellij/util/MapstructElementUtils.java b/src/main/java/org/mapstruct/intellij/util/MapstructElementUtils.java index 7d041c20..ac0c5cdc 100644 --- a/src/main/java/org/mapstruct/intellij/util/MapstructElementUtils.java +++ b/src/main/java/org/mapstruct/intellij/util/MapstructElementUtils.java @@ -69,6 +69,25 @@ public static PsiJavaElementPattern.Capture beanMappingElementPatter return elementPattern( parameterName, MapstructUtil.BEAN_MAPPING_FQN ); } + /** + * @param parameterName the name of the parameter in the {@code @InheritConfiguration} annotation + * + * @return an element pattern for a parameter in the {@code @InheritConfiguration} annotation + */ + public static PsiJavaElementPattern.Capture inheritConfigurationElementPattern(String parameterName) { + return elementPattern( parameterName, MapstructUtil.INHERIT_CONFIGURATION_FQN ); + } + + /** + * @param parameterName the name of the parameter in the {@code @InheritInverseConfiguration} annotation + * + * @return an element pattern for a parameter in the {@code @InheritInverseConfiguration} annotation + */ + public static PsiJavaElementPattern.Capture inheritInverseConfigurationElementPattern( + String parameterName) { + return elementPattern( parameterName, MapstructUtil.INHERIT_INVERSE_CONFIGURATION_FQN ); + } + private static PsiJavaElementPattern.Capture elementPattern(String parameterName, String annotationFQN) { return psiElement() diff --git a/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java b/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java index ceb4559a..cad8689d 100644 --- a/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java +++ b/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java @@ -6,11 +6,14 @@ package org.mapstruct.intellij.util; import java.beans.Introspector; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Function; +import java.util.stream.Collectors; import java.util.stream.Stream; import javax.swing.Icon; @@ -139,6 +142,29 @@ public static LookupElement asLookup(PsiEnumConstant enumConstant) { return asLookup( enumConstant.getName(), enumConstant, PsiField::getType, PlatformIcons.FIELD_ICON ); } + public static LookupElement asLookup(@NotNull PsiMethod method, @NotNull String lookupString, + @NotNull String representableText) { + return asLookupWithRepresentableText( + method, + lookupString, + representableText, + String.format( + " %s#%s(%s)", + Objects.requireNonNull( method.getContainingClass() ).getName(), + method.getName(), + formatParameters( method ) + ) + ); + } + + @NotNull + private static String formatParameters(@NotNull PsiMethod method) { + return Arrays.stream( method.getParameterList().getParameters() ) + .map( PsiParameter::getType ) + .map( PsiType::getPresentableText ) + .collect( Collectors.joining( ", " ) ); + } + public static LookupElement asLookupWithRepresentableText(PsiMethod method, String lookupString, String representableText, String tailText) { LookupElementBuilder builder = LookupElementBuilder.create( method, lookupString ) @@ -294,7 +320,7 @@ public static String getPropertyName(@NotNull PsiMethod method) { @NotNull @NonNls public static String getPropertyName(@NotNull String methodName) { - String name = ""; + String name; if ( methodName.startsWith( "is" ) ) { name = Introspector.decapitalize( methodName.substring( 2 ) ); } diff --git a/src/test/java/org/mapstruct/intellij/completion/InheritConfigurationCompletionTestCase.java b/src/test/java/org/mapstruct/intellij/completion/InheritConfigurationCompletionTestCase.java new file mode 100644 index 00000000..a8114c40 --- /dev/null +++ b/src/test/java/org/mapstruct/intellij/completion/InheritConfigurationCompletionTestCase.java @@ -0,0 +1,53 @@ +/* + * 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.completion; + +import com.intellij.codeInsight.lookup.LookupElement; +import org.mapstruct.intellij.MapstructBaseCompletionTestCase; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Oliver Erhart + */ +public class InheritConfigurationCompletionTestCase extends MapstructBaseCompletionTestCase { + + @Override + protected String getTestDataPath() { + return "testData/completion/inherit"; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + addDirectoryToProject( "../../mapping/dto" ); + } + + public void testInheritMultipleMethodsCarMapper() { + configureByTestName(); + + assertThat( myItems ) + .extracting( LookupElement::getLookupString ) + .containsExactlyInAnyOrder( + "mapTo", + "mapToBase" + ); + } + + public void testInheritReverseWithExplicitInheritance() { + configureByTestName(); + + assertThat( myItems ) + .extracting( LookupElement::getLookupString ) + .containsExactlyInAnyOrder( + "toCarEntity", + "toCarEntityWithFixedAuditTrail", + "baseDtoToEntity" + ); + } + +} diff --git a/testData/completion/inherit/InheritMultipleMethodsCarMapper.java b/testData/completion/inherit/InheritMultipleMethodsCarMapper.java new file mode 100644 index 00000000..f809cc68 --- /dev/null +++ b/testData/completion/inherit/InheritMultipleMethodsCarMapper.java @@ -0,0 +1,40 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.ap.test.complex; + +import org.mapstruct.InheritConfiguration; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.factory.Mappers; +import org.example.dto.CarDto; +import org.example.dto.Car; + +@Mapper +public abstract class InheritMultipleMethodsCarMapper { + + @Mappings({ + @Mapping(target = "maker", source = "manufacturer"), + @Mapping(target = "seatCount", source = "numberOfSeats") + }) + public abstract CarDto mapToBase(Car car); + + @Mappings({ + @Mapping(target = "manufacturer", constant = "food"), + @Mapping(target = "numberOfSeats", ignore = true) + }) + public abstract Car mapFromBase(CarDto carDto); + + @InheritConfiguration(name = "mapToBase") + @InheritInverseConfiguration(name = "mapFromBase") + public abstract CarDto mapTo(Car car); + + @InheritConfiguration(name = "mapFromBase") + @InheritInverseConfiguration(name = "") + public abstract Car mapFrom(CarDto carDto); + +} diff --git a/testData/completion/inherit/InheritReverseWithExplicitInheritance.java b/testData/completion/inherit/InheritReverseWithExplicitInheritance.java new file mode 100644 index 00000000..5ce559ea --- /dev/null +++ b/testData/completion/inherit/InheritReverseWithExplicitInheritance.java @@ -0,0 +1,70 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0 + */ +import org.mapstruct.InheritConfiguration; +import org.mapstruct.InheritInverseConfiguration; +import org.mapstruct.Mapper; +import org.mapstruct.MapperConfig; +import org.mapstruct.Mapping; +import org.mapstruct.MappingInheritanceStrategy; +import org.mapstruct.MappingTarget; + +@MapperConfig( + mappingInheritanceStrategy = MappingInheritanceStrategy.AUTO_INHERIT_FROM_CONFIG +) +interface AutoInheritedConfig { + + @Mapping(target = "primaryKey", source = "id") + @Mapping(target = "auditTrail", ignore = true) + BaseVehicleEntity baseDtoToEntity(BaseVehicleDto dto); +} + +@Mapper( + uses = NotToBeUsedMapper.class, + config = AutoInheritedConfig.class, + mappingInheritanceStrategy = MappingInheritanceStrategy.EXPLICIT +) +abstract class CarMapperWithExplicitInheritance { + + @InheritConfiguration(name = "baseDtoToEntity") + @Mapping(target = "color", source = "colour") + abstract CarEntity toCarEntity(CarDto carDto); + + @InheritInverseConfiguration(name = "") + abstract CarDto toCarDto(CarEntity entity); + + @InheritConfiguration(name = "toCarEntity") + @Mapping(target = "auditTrail", constant = "fixed") + abstract CarEntity toCarEntityWithFixedAuditTrail(CarDto carDto); + + // this method should not be considered. See https://github.com/mapstruct/mapstruct/issues/1013 + void toCarEntity(CarDto carDto, @MappingTarget CarEntity carEntity) { } +} + +abstract class BaseVehicleEntity { + public long primaryKey; + public String auditTrail; +} + +abstract class BaseVehicleDto { + public long id; +} + +class CarDto extends BaseVehicleDto { + public String colour; +} + +class CarEntity extends BaseVehicleEntity { + public String color; +} + +@Mapper +interface NotToBeUsedMapper { + + @Mapping(target = "primaryKey", ignore = true) + @Mapping(target = "auditTrail", ignore = true) + @Mapping(target = "color", ignore = true) + CarEntity toCarEntityNotUsed(CarDto carDto); +} \ No newline at end of file