diff --git a/README.md b/README.md index 000ea87..bd55e48 100644 --- a/README.md +++ b/README.md @@ -24,11 +24,13 @@ To learn more about MapStruct have a look at the [mapstruct](https://github.com/ * Code completions * Completion of `target` and `source` properties in `@Mapping` annotation (nested properties also work) * Completion of `target` and `source` properties in `@ValueMapping` annotation + * Completion of `targets` in `@Ignored` annotation with `prefix` support * Completion of `componentModel` in `@Mapper` and `@MapperConfig` annotations * Completion of `qualifiedByName` in `@Mapping` annotation * Go To Declaration for properties in `target` and `source` to setters / getters +* Go To Declaration for `targets` in `@Ignored` annotation * Go To Declaration for `Mapping#qualifiedByName` -* Find usages of properties in `target` and `source` and find usages of setters / getters in `@Mapping` annotations +* Find usages of properties in `target` and `source` and find usages of setters / getters in `@Mapping` and `@Ignored` annotations * Highlighting properties in `target` and `source` * Errors and Quick fixes: * `@Mapper` or `@MapperConfig` annotation missing @@ -37,9 +39,10 @@ To learn more about MapStruct have a look at the [mapstruct](https://github.com/ * No `source` defined in `@Mapping` annotation * More than one `source` in `@Mapping` annotation defined with quick fixes: Remove `source`. Remove `constant`. Remove `expression`. Use `constant` as `defaultValue`. Use `expression` as `defaultExpression`. * More than one default source in `@Mapping` annotation defined with quick fixes: Remove `defaultValue`. Remove `defaultExpression`. - * `target` mapped more than once by `@Mapping` annotations with quick fixes: Remove annotation and change target property. + * `target` mapped more than once by `@Mapping` and/or `@Ignored` annotations with quick fixes: Remove annotation and change target property. * `*` used as a source in `@Mapping` annotation with quick fixes: Replace `*` with method parameter name. - * Unknown reference inspection for `source` and `target` in `@Mapping` and `@ValueMapping` annotation. + * Unknown reference inspection for `source` and `target` in `@Mapping` and `@ValueMapping` annotation. + * Unknown reference inspection for `targets` and `prefix` in `@Ignored` annotation. * Unknown reference inspection for `qualifiedByName` in `@Mapping` annotation ## Requirements diff --git a/build.gradle b/build.gradle index d21b9d8..9931651 100644 --- a/build.gradle +++ b/build.gradle @@ -111,13 +111,13 @@ dependencies { testFramework( TestFrameworkType.Platform.INSTANCE ) testFramework( TestFrameworkType.Bundled.INSTANCE ) } - implementation('org.mapstruct:mapstruct:1.5.3.Final') + implementation('org.mapstruct:mapstruct:1.7.0.Beta1') testImplementation(platform('org.junit:junit-bom:5.11.0')) testImplementation('org.junit.platform:junit-platform-launcher') testImplementation('org.junit.jupiter:junit-jupiter-api') testImplementation('org.junit.jupiter:junit-jupiter-engine') testRuntimeOnly('org.junit.vintage:junit-vintage-engine') - testImplementation('org.assertj:assertj-core:3.26.3') + testImplementation('org.assertj:assertj-core:3.27.7') testImplementation('org.apache.commons:commons-text:1.15.0') testImplementation( 'junit:junit:4.13.2' ) testRuntimeOnly('org.immutables:value:2.10.1') @@ -130,7 +130,9 @@ tasks.register('libs', Sync) { include('mapstruct-intellij-*.jar') include('MapStruct-Intellij-*.jar') } - rename('mapstruct-1.5.3.Final.jar', 'mapstruct.jar') + rename { String fileName -> + return fileName.startsWith('mapstruct-') ? 'mapstruct.jar' : fileName + } } tasks.register('testLibs', Sync) { diff --git a/change-notes.html b/change-notes.html index 722f1a3..9956473 100644 --- a/change-notes.html +++ b/change-notes.html @@ -1,4 +1,16 @@ +

1.10.0

+

1.9.1

diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseTargetReference.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseTargetReference.java new file mode 100644 index 0000000..efa2974 --- /dev/null +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/BaseTargetReference.java @@ -0,0 +1,209 @@ +/* + * 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.Map; +import java.util.stream.Stream; + +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiClass; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiField; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiParameter; +import com.intellij.psi.PsiParameterList; +import com.intellij.psi.PsiRecordComponent; +import com.intellij.psi.PsiSubstitutor; +import com.intellij.psi.PsiType; +import com.intellij.psi.PsiVariable; +import com.intellij.psi.util.PsiUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.mapstruct.intellij.util.MapStructVersion; +import org.mapstruct.intellij.util.MapstructUtil; +import org.mapstruct.intellij.util.TargetType; +import org.mapstruct.intellij.util.TargetUtils; + +import static org.mapstruct.intellij.util.MapstructUtil.asLookup; +import static org.mapstruct.intellij.util.MapstructUtil.findRecordComponent; +import static org.mapstruct.intellij.util.MapstructUtil.isPublicModifiable; +import static org.mapstruct.intellij.util.MapstructUtil.isPublicNonStatic; +import static org.mapstruct.intellij.util.TargetUtils.findAllDefinedMappingTargets; +import static org.mapstruct.intellij.util.TargetUtils.findAllIgnoredTargets; +import static org.mapstruct.intellij.util.TargetUtils.isBuilderEnabled; +import static org.mapstruct.intellij.util.TargetUtils.publicWriteAccessors; +import static org.mapstruct.intellij.util.TargetUtils.resolveBuilderOrSelfClass; +import static org.mapstruct.intellij.util.TypeUtils.firstParameterPsiType; + +/** + * Base class for target references ({@link MapstructTargetReference} and {@link MapstructIgnoredTargetReference}). + * Provides shared implementations for resolving the type of target elements. + * + * @author Filip Hrisafov + */ +abstract class BaseTargetReference extends BaseMappingReference { + + protected final MapStructVersion mapStructVersion; + + BaseTargetReference(@NotNull PsiElement element, @Nullable MapstructBaseReference previousReference, + TextRange rangeInElement, String value) { + super( element, previousReference, rangeInElement, value ); + mapStructVersion = MapstructUtil.resolveMapStructProjectVersion( element.getContainingFile() + .getOriginalFile() ); + } + + @Override + PsiElement resolveInternal(@NotNull String value, @NotNull PsiType psiType) { + return resolveTargetElement( value, psiType, getMappingMethod() ); + } + + PsiElement resolveTargetElement(@NotNull String value, @NotNull PsiType psiType, + @Nullable PsiMethod mappingMethod) { + boolean builderSupportPresent = mapStructVersion.isBuilderSupported(); + Pair pair = resolveBuilderOrSelfClass( + psiType, + builderSupportPresent && isBuilderEnabled( mappingMethod ) + ); + if ( pair == null ) { + return null; + } + + PsiClass psiClass = pair.getFirst(); + TargetType targetType = pair.getSecond(); + PsiType typeToUse = targetType.type(); + + PsiRecordComponent recordComponent = findRecordComponent( value, psiClass ); + if ( recordComponent != null ) { + return recordComponent; + } + + if ( mapStructVersion.isConstructorSupported() && !targetType.builder() ) { + PsiMethod constructor = TargetUtils.resolveMappingConstructor( psiClass ); + if ( constructor != null && constructor.hasParameters() ) { + for ( PsiParameter parameter : constructor.getParameterList().getParameters() ) { + if ( value.equals( parameter.getName() ) ) { + return parameter; + } + } + } + } + + String capitalizedName = MapstructUtil.capitalize( value ); + PsiMethod[] methods = psiClass.findMethodsByName( "set" + capitalizedName, true ); + if ( methods.length != 0 && isPublicNonStatic( methods[0] ) ) { + return methods[0]; + } + + // If there is no such setter we need to check if there is a collection getter + methods = psiClass.findMethodsByName( "get" + capitalizedName, true ); + if ( methods.length != 0 && isCollectionGetterWriteAccessor( methods[0] ) ) { + return methods[0]; + } + + if ( builderSupportPresent ) { + for ( Pair builderPair : psiClass.findMethodsAndTheirSubstitutorsByName( + value, + true + ) ) { + PsiMethod method = builderPair.getFirst(); + if ( method.getParameterList().getParametersCount() == 1 && + mapstructUtil.isFluentSetter( method, typeToUse, builderPair.getSecond() ) ) { + return method; + } + } + } + + PsiClass selfClass = PsiUtil.resolveClassInType( psiType ); + if ( selfClass != null ) { + PsiField field = selfClass.findFieldByName( value, true ); + if ( field != null && isPublicModifiable( field ) ) { + return field; + } + } + + return null; + } + + @NotNull + @Override + Object[] getVariantsInternal(@NotNull PsiType psiType) { + + PsiMethod mappingMethod = getMappingMethod(); + + Map> accessors = publicWriteAccessors( + psiType, + mapStructVersion, + mapstructUtil, + mappingMethod + ); + + if ( mappingMethod != null ) { + findAllDefinedTargets( mappingMethod ).forEach( accessors::remove ); + } + + return asLookup( + accessors, + BaseTargetReference::memberPsiType + ); + } + + protected Stream findAllDefinedTargets(PsiMethod mappingMethod) { + return Stream.concat( + findAllDefinedMappingTargets( mappingMethod, mapStructVersion ), + findAllIgnoredTargets( mappingMethod ) + ); + } + + @Nullable + @Override + PsiType resolvedType() { + PsiElement element = resolve(); + if ( element instanceof PsiMethod psiMethod ) { + return firstParameterPsiType( psiMethod ); + } + else if ( element instanceof PsiParameter psiParameter ) { + return psiParameter.getType(); + } + else if ( element instanceof PsiRecordComponent psiRecordComponent ) { + return psiRecordComponent.getType(); + } + else if ( element instanceof PsiField psiField ) { + return psiField.getType(); + } + + return null; + } + + static PsiType memberPsiType(PsiElement psiMember) { + if ( psiMember instanceof PsiMethod psiMemberMethod ) { + return resolveMethodType( psiMemberMethod ); + } + else if ( psiMember instanceof PsiVariable psiMemberVariable ) { + return psiMemberVariable.getType(); + } + return null; + } + + static PsiType resolveMethodType(PsiMethod psiMethod) { + PsiParameter[] psiParameters = psiMethod.getParameterList().getParameters(); + if ( psiParameters.length == 0 ) { + return psiMethod.getReturnType(); + } + return psiParameters[0].getType(); + } + + private static boolean isCollectionGetterWriteAccessor(@NotNull PsiMethod method) { + if ( !isPublicNonStatic( method ) ) { + return false; + } + PsiParameterList parameterList = method.getParameterList(); + if ( parameterList.getParametersCount() > 0 ) { + return false; + } + return TargetUtils.isMethodReturnTypeAssignableToCollectionOrMap( method ); + } +} diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructIgnoredTargetReference.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructIgnoredTargetReference.java new file mode 100644 index 0000000..18fdc00 --- /dev/null +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructIgnoredTargetReference.java @@ -0,0 +1,108 @@ +/* + * 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.stream.Stream; + +import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.codeInsight.lookup.LookupElement; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.PsiAnnotation; +import com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiReference; +import com.intellij.psi.PsiType; +import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.mapstruct.intellij.util.TargetUtils; + +import static org.mapstruct.intellij.util.MapstructUtil.canDescendIntoType; +import static org.mapstruct.intellij.util.TargetUtils.getRelevantType; + +/** + * Reference for {@link org.mapstruct.Ignored#targets()}. + * + * @author Filip Hrisafov + */ +class MapstructIgnoredTargetReference extends BaseTargetReference { + + private MapstructIgnoredTargetReference(PsiElement element, MapstructIgnoredTargetReference previousReference, + TextRange rangeInElement, String value) { + super( element, previousReference, rangeInElement, value ); + } + + @Nullable + @Override + PsiElement resolveInternal(@NotNull String value, @NotNull PsiMethod mappingMethod) { + PsiType targetType = resolveIgnoredTargetsBaseType( mappingMethod ); + return targetType == null ? null : resolveInternal( value, targetType ); + } + + @Override + protected Stream findAllDefinedTargets(PsiMethod mappingMethod) { + PsiAnnotation annotation = PsiTreeUtil.getParentOfType( getElement(), PsiAnnotation.class ); + + Stream allTargets = super.findAllDefinedTargets( mappingMethod ); + + String prefixDot = TargetUtils.getIgnoredPrefix( annotation ); + + if ( !prefixDot.isEmpty() ) { + allTargets = allTargets + .filter( target -> target.startsWith( prefixDot ) ) + .map( target -> target.substring( prefixDot.length() ) ); + } + return allTargets; + } + + @NotNull + @Override + Object[] getVariantsInternal(@NotNull PsiMethod mappingMethod) { + PsiType targetType = resolveIgnoredTargetsBaseType( mappingMethod ); + return targetType == null ? LookupElement.EMPTY_ARRAY : getVariantsInternal( targetType ); + } + + static PsiReference[] create(PsiElement psiElement) { + return MapstructBaseReference.create( psiElement, MapstructIgnoredTargetReference::new, true ); + } + + @Nullable + private PsiType resolveIgnoredTargetsBaseType(@NotNull PsiMethod mappingMethod) { + PsiType targetType = getRelevantType( mappingMethod ); + if ( targetType == null ) { + return null; + } + + PsiAnnotation annotation = PsiTreeUtil.getParentOfType( getElement(), PsiAnnotation.class ); + if ( annotation == null ) { + return targetType; + } + + String prefix = AnnotationUtil.getDeclaredStringAttributeValue( annotation, "prefix" ); + if ( prefix == null || prefix.isEmpty() ) { + return targetType; + } + + PsiElement prefixElement = annotation.findDeclaredAttributeValue( "prefix" ); + if ( prefixElement == null ) { + return targetType; + } + + PsiReference[] prefixReferences = MapstructTargetReference.create( prefixElement ); + if ( prefixReferences.length == 0 ) { + return null; + } + + PsiReference lastReference = prefixReferences[prefixReferences.length - 1]; + if ( lastReference instanceof MapstructBaseReference baseReference ) { + PsiType resolvedType = baseReference.resolvedType(); + return canDescendIntoType( resolvedType ) ? resolvedType : null; + } + + return null; + } + +} diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructKotlinReferenceContributor.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructKotlinReferenceContributor.java index 68155d5..4dc15d4 100644 --- a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructKotlinReferenceContributor.java +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructKotlinReferenceContributor.java @@ -10,6 +10,7 @@ import org.jetbrains.annotations.NotNull; import static org.mapstruct.intellij.util.MapstructKotlinElementUtils.beanMappingElementPattern; +import static org.mapstruct.intellij.util.MapstructKotlinElementUtils.ignoredElementPattern; import static org.mapstruct.intellij.util.MapstructKotlinElementUtils.mappingElementPattern; import static org.mapstruct.intellij.util.MapstructKotlinElementUtils.valueMappingElementPattern; @@ -31,6 +32,14 @@ public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) mappingElementPattern( "source" ), new MappingTargetReferenceProvider( MapstructSourceReference::create ) ); + registrar.registerReferenceProvider( + ignoredElementPattern( "prefix" ), + new MappingTargetReferenceProvider( MapstructTargetReference::create ) + ); + registrar.registerReferenceProvider( + ignoredElementPattern( "targets" ), + new MappingTargetReferenceProvider( MapstructIgnoredTargetReference::create ) + ); registrar.registerReferenceProvider( beanMappingElementPattern( "ignoreUnmappedSourceProperties" ), new MappingTargetReferenceProvider( MapstructSourceReference::createNonNested ) 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 f2edb1f..d097b94 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,7 @@ import org.jetbrains.annotations.NotNull; import static org.mapstruct.intellij.util.MapstructElementUtils.beanMappingElementPattern; +import static org.mapstruct.intellij.util.MapstructElementUtils.ignoredElementPattern; import static org.mapstruct.intellij.util.MapstructElementUtils.mappingElementPattern; import static org.mapstruct.intellij.util.MapstructElementUtils.valueMappingElementPattern; @@ -34,6 +35,14 @@ public void registerReferenceProviders(@NotNull PsiReferenceRegistrar registrar) mappingElementPattern( "source" ), new MappingTargetReferenceProvider( MapstructSourceReference::create ) ); + registrar.registerReferenceProvider( + ignoredElementPattern( "prefix" ), + new MappingTargetReferenceProvider( MapstructTargetReference::create ) + ); + registrar.registerReferenceProvider( + ignoredElementPattern( "targets" ), + new MappingTargetReferenceProvider( MapstructIgnoredTargetReference::create ) + ); registrar.registerReferenceProvider( beanMappingElementPattern( "ignoreUnmappedSourceProperties" ), diff --git a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructTargetReference.java b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructTargetReference.java index ca54a8a..3bb57bb 100644 --- a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructTargetReference.java +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructTargetReference.java @@ -5,53 +5,26 @@ */ package org.mapstruct.intellij.codeinsight.references; -import java.util.Map; import java.util.Objects; import java.util.stream.Stream; -import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.lookup.LookupElement; -import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.TextRange; -import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; -import com.intellij.psi.PsiField; import com.intellij.psi.PsiMethod; -import com.intellij.psi.PsiParameter; -import com.intellij.psi.PsiParameterList; -import com.intellij.psi.PsiRecordComponent; import com.intellij.psi.PsiReference; -import com.intellij.psi.PsiSubstitutor; import com.intellij.psi.PsiType; -import com.intellij.psi.PsiVariable; -import com.intellij.psi.util.PsiUtil; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.mapstruct.Mapping; -import org.mapstruct.intellij.util.MapStructVersion; import org.mapstruct.intellij.util.MapstructUtil; -import org.mapstruct.intellij.util.TargetType; -import org.mapstruct.intellij.util.TargetUtils; -import static org.mapstruct.intellij.util.MapstructAnnotationUtils.findAllDefinedMappingAnnotations; -import static org.mapstruct.intellij.util.MapstructUtil.asLookup; -import static org.mapstruct.intellij.util.MapstructUtil.findRecordComponent; -import static org.mapstruct.intellij.util.MapstructUtil.isPublicModifiable; -import static org.mapstruct.intellij.util.MapstructUtil.isPublicNonStatic; import static org.mapstruct.intellij.util.TargetUtils.getRelevantType; -import static org.mapstruct.intellij.util.TargetUtils.isBuilderEnabled; -import static org.mapstruct.intellij.util.TargetUtils.publicWriteAccessors; -import static org.mapstruct.intellij.util.TargetUtils.resolveBuilderOrSelfClass; -import static org.mapstruct.intellij.util.TypeUtils.firstParameterPsiType; /** * Reference for {@link org.mapstruct.Mapping#target()}. * * @author Filip Hrisafov */ -class MapstructTargetReference extends BaseMappingReference { - - private final MapStructVersion mapStructVersion; +class MapstructTargetReference extends BaseTargetReference { /** * Create a new {@link MapstructTargetReference} with the provided parameters @@ -64,75 +37,6 @@ class MapstructTargetReference extends BaseMappingReference { private MapstructTargetReference(PsiElement element, MapstructTargetReference previousReference, TextRange rangeInElement, String value) { super( element, previousReference, rangeInElement, value ); - mapStructVersion = MapstructUtil.resolveMapStructProjectVersion( element.getContainingFile() - .getOriginalFile() ); - } - - @Override - PsiElement resolveInternal(@NotNull String value, @NotNull PsiType psiType) { - boolean builderSupportPresent = mapStructVersion.isBuilderSupported(); - Pair pair = resolveBuilderOrSelfClass( - psiType, - builderSupportPresent && isBuilderEnabled( getMappingMethod() ) - ); - if ( pair == null ) { - return null; - } - - PsiClass psiClass = pair.getFirst(); - TargetType targetType = pair.getSecond(); - PsiType typeToUse = targetType.type(); - - PsiRecordComponent recordComponent = findRecordComponent( value, psiClass ); - if ( recordComponent != null ) { - return recordComponent; - } - - if ( mapStructVersion.isConstructorSupported() && !targetType.builder() ) { - PsiMethod constructor = TargetUtils.resolveMappingConstructor( psiClass ); - if ( constructor != null && constructor.hasParameters() ) { - for ( PsiParameter parameter : constructor.getParameterList().getParameters() ) { - if ( value.equals( parameter.getName() ) ) { - return parameter; - } - } - } - } - - String capitalizedName = MapstructUtil.capitalize( value ); - PsiMethod[] methods = psiClass.findMethodsByName( "set" + capitalizedName, true ); - if ( methods.length != 0 && isPublicNonStatic( methods[0] ) ) { - return methods[0]; - } - - // If there is no such setter we need to check if there is a collection getter - methods = psiClass.findMethodsByName( "get" + capitalizedName, true ); - if ( methods.length != 0 && isCollectionGetterWriteAccessor( methods[0] ) ) { - return methods[0]; - } - - if ( builderSupportPresent ) { - for ( Pair builderPair : psiClass.findMethodsAndTheirSubstitutorsByName( - value, - true - ) ) { - PsiMethod method = builderPair.getFirst(); - if ( method.getParameterList().getParametersCount() == 1 && - mapstructUtil.isFluentSetter( method, typeToUse, builderPair.getSecond() ) ) { - return method; - } - } - } - - PsiClass selfClass = PsiUtil.resolveClassInType( psiType ); - if ( selfClass != null ) { - PsiField field = selfClass.findFieldByName( value, true ); - if ( field != null && isPublicModifiable( field ) ) { - return field; - } - } - - return null; } @Override @@ -142,7 +46,11 @@ PsiElement resolveInternal(@NotNull String value, @NotNull PsiMethod mappingMeth return null; } - PsiElement psiElement = resolveInternal( value, relevantType ); + PsiElement psiElement = resolveTargetElement( + value, + relevantType, + mappingMethod + ); if ( psiElement != null ) { return psiElement; } @@ -154,44 +62,6 @@ PsiElement resolveInternal(@NotNull String value, @NotNull PsiMethod mappingMeth .orElse( null ); } - @NotNull - @Override - Object[] getVariantsInternal(@NotNull PsiType psiType) { - - PsiMethod mappingMethod = getMappingMethod(); - - Map> accessors = publicWriteAccessors( - psiType, - mapStructVersion, - mapstructUtil, - mappingMethod - ); - - if (mappingMethod != null) { - Stream allDefinedMappingTargets = findAllDefinedMappingTargets( mappingMethod ); - allDefinedMappingTargets.forEach( accessors::remove ); - } - - return asLookup( - accessors, - MapstructTargetReference::memberPsiType - ); - } - - /** - * Find all defined {@link Mapping#target()} for the given method - * - * @param method that needs to be checked - * - * @return see description - */ - private Stream findAllDefinedMappingTargets(@NotNull PsiMethod method) { - return findAllDefinedMappingAnnotations( method, mapStructVersion ) - .map( psiAnnotation -> AnnotationUtil.getDeclaredStringAttributeValue( psiAnnotation, "target" ) ) - .filter( Objects::nonNull ) - .filter( s -> !s.isEmpty() ); - } - @NotNull @Override Object[] getVariantsInternal(@NotNull PsiMethod mappingMethod) { @@ -199,27 +69,6 @@ Object[] getVariantsInternal(@NotNull PsiMethod mappingMethod) { return targetType == null ? LookupElement.EMPTY_ARRAY : getVariantsInternal( targetType ); } - @Nullable - @Override - PsiType resolvedType() { - PsiElement element = resolve(); - - if ( element instanceof PsiMethod psiMethod ) { - return firstParameterPsiType( psiMethod ); - } - else if ( element instanceof PsiParameter psiParameter ) { - return psiParameter.getType(); - } - else if ( element instanceof PsiRecordComponent psiRecordComponent ) { - return psiRecordComponent.getType(); - } - else if ( element instanceof PsiField psiField ) { - return psiField.getType(); - } - - return null; - } - /** * @param psiElement the literal for which references need to be created * @@ -229,35 +78,4 @@ static PsiReference[] create(PsiElement psiElement) { return MapstructBaseReference.create( psiElement, MapstructTargetReference::new, true ); } - private static PsiType memberPsiType(PsiElement psiMember) { - if ( psiMember instanceof PsiMethod psiMemberMethod ) { - return resolveMethodType( psiMemberMethod ); - } - else if ( psiMember instanceof PsiVariable psiMemberVariable ) { - return psiMemberVariable.getType(); - } - else { - return null; - } - - } - - private static boolean isCollectionGetterWriteAccessor(@NotNull PsiMethod method) { - if ( !isPublicNonStatic( method ) ) { - return false; - } - PsiParameterList parameterList = method.getParameterList(); - if ( parameterList.getParametersCount() > 0 ) { - return false; - } - return TargetUtils.isMethodReturnTypeAssignableToCollectionOrMap( method ); - } - - private static PsiType resolveMethodType(PsiMethod psiMethod) { - PsiParameter[] psiParameters = psiMethod.getParameterList().getParameters(); - if ( psiParameters.length == 0 ) { - return psiMethod.getReturnType(); - } - return psiParameters[0].getType(); - } } diff --git a/src/main/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspection.java b/src/main/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspection.java index 2c5aade..9fa63bd 100644 --- a/src/main/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspection.java +++ b/src/main/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspection.java @@ -5,6 +5,14 @@ */ package org.mapstruct.intellij.inspection; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.intention.QuickFixFactory; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement; @@ -38,15 +46,10 @@ import org.mapstruct.intellij.util.MapstructUtil; import org.mapstruct.intellij.util.TargetUtils; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; - import static com.intellij.codeInsight.AnnotationUtil.getStringAttributeValue; -import static org.mapstruct.intellij.util.MapstructAnnotationUtils.extractMappingAnnotationsFromMappings; +import static org.mapstruct.intellij.util.MapstructAnnotationUtils.extractRepeatableAnnotations; +import static org.mapstruct.intellij.util.MapstructUtil.IGNORED_ANNOTATION_FQN; +import static org.mapstruct.intellij.util.MapstructUtil.IGNORED_LIST_ANNOTATION_FQN; import static org.mapstruct.intellij.util.MapstructUtil.MAPPINGS_ANNOTATION_FQN; import static org.mapstruct.intellij.util.MapstructUtil.MAPPING_ANNOTATION_FQN; import static org.mapstruct.intellij.util.TargetUtils.getTargetType; @@ -87,9 +90,16 @@ public void visitMethod(PsiMethod method) { handleMappingAnnotation( psiAnnotation, problemMap ); } else if (MAPPINGS_ANNOTATION_FQN.equals( qualifiedName )) { - extractMappingAnnotationsFromMappings( psiAnnotation ) + extractRepeatableAnnotations( psiAnnotation ) .forEach( a -> handleMappingAnnotation( a, problemMap ) ); } + else if ( IGNORED_ANNOTATION_FQN.equals( qualifiedName ) ) { + handleIgnoredAnnotation( psiAnnotation, problemMap ); + } + else if ( IGNORED_LIST_ANNOTATION_FQN.equals( qualifiedName ) ) { + extractRepeatableAnnotations( psiAnnotation ) + .forEach( a -> handleIgnoredAnnotation( a, problemMap ) ); + } else { // Handle annotations containing at least one Mapping annotation handleAnnotationWithMappingAnnotation( psiAnnotation, problemMap ); @@ -155,6 +165,26 @@ private static void handleMappingAnnotation(PsiAnnotation psiAnnotation, } } + private static void handleIgnoredAnnotation(PsiAnnotation psiAnnotation, + Map> problemMap) { + String prefixDot = TargetUtils.getIgnoredPrefix( psiAnnotation ); + + PsiAnnotationMemberValue targetsValue = psiAnnotation.findDeclaredAttributeValue( "targets" ); + for ( PsiAnnotationMemberValue value : AnnotationUtil.arrayAttributeValues( targetsValue ) ) { + addIgnoredTarget( value, prefixDot, problemMap ); + } + + } + + private static void addIgnoredTarget(PsiAnnotationMemberValue value, String prefixDot, + Map> problemMap) { + String target = getStringAttributeValue( value ); + if ( target != null && !target.isEmpty() ) { + problemMap.computeIfAbsent( prefixDot + target, k -> new ArrayList<>() ) + .add( value ); + } + } + private static class ChangeTargetQuickFix extends LocalQuickFixOnPsiElement { private final String myText; diff --git a/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java b/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java index bc61b1d..80fd1cc 100644 --- a/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java +++ b/src/main/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesInspection.java @@ -46,6 +46,7 @@ import static org.mapstruct.intellij.util.SourceUtils.findAllSourceProperties; import static org.mapstruct.intellij.util.SourceUtils.getGenericTypes; import static org.mapstruct.intellij.util.TargetUtils.findAllDefinedMappingTargets; +import static org.mapstruct.intellij.util.TargetUtils.findAllIgnoredTargets; import static org.mapstruct.intellij.util.TargetUtils.findAllSourcePropertiesForCurrentTarget; import static org.mapstruct.intellij.util.TargetUtils.findAllTargetProperties; import static org.mapstruct.intellij.util.TargetUtils.getTargetType; @@ -117,6 +118,12 @@ public void visitMethod(PsiMethod method) { .collect( Collectors.toSet() ); allTargetProperties.removeAll( definedTargets ); + // find and remove all @Ignored targets + Set ignoredTargets = findAllIgnoredTargets( method ) + .map( MyJavaElementVisitor::getBaseTarget ) + .collect( Collectors.toSet() ); + allTargetProperties.removeAll( ignoredTargets ); + // find and remove all inherited target properties Set inheritedTargetProperties = findInheritedTargetProperties( method, mapStructVersion ) .map( MyJavaElementVisitor::getBaseTarget ) diff --git a/src/main/java/org/mapstruct/intellij/util/MapStructVersion.java b/src/main/java/org/mapstruct/intellij/util/MapStructVersion.java index 77e69b5..f446a45 100644 --- a/src/main/java/org/mapstruct/intellij/util/MapStructVersion.java +++ b/src/main/java/org/mapstruct/intellij/util/MapStructVersion.java @@ -12,7 +12,8 @@ public enum MapStructVersion { V1_2_O( false, false ), V1_3_O( true, false ), - V1_4_O( true, true ); + V1_4_O( true, true ), + V1_7_O( true, true ); private final boolean builderSupported; private final boolean constructorSupported; diff --git a/src/main/java/org/mapstruct/intellij/util/MapstructAnnotationUtils.java b/src/main/java/org/mapstruct/intellij/util/MapstructAnnotationUtils.java index bff2730..43eb11d 100644 --- a/src/main/java/org/mapstruct/intellij/util/MapstructAnnotationUtils.java +++ b/src/main/java/org/mapstruct/intellij/util/MapstructAnnotationUtils.java @@ -298,26 +298,26 @@ private static Stream findAllDefinedMappingAnnotations(@NotNull P boolean includeMetaAnnotations) { //TODO cache PsiAnnotation mappings = findAnnotation( owner, true, MapstructUtil.MAPPINGS_ANNOTATION_FQN ); - Stream mappingsAnnotations = extractMappingAnnotationsFromMappings( mappings ); + Stream mappingsAnnotations = extractRepeatableAnnotations( mappings ); Stream mappingAnnotations = findMappingAnnotations( owner, includeMetaAnnotations ); return Stream.concat( mappingAnnotations, mappingsAnnotations ); } @NotNull - public static Stream extractMappingAnnotationsFromMappings(@Nullable PsiAnnotation mappings) { - if (mappings == null) { + public static Stream extractRepeatableAnnotations(@Nullable PsiAnnotation annotation) { + if ( annotation == null ) { return Stream.empty(); } //TODO maybe there is a better way to do this, but currently I don't have that much knowledge - PsiAnnotationMemberValue mappingsValue = mappings.findDeclaredAttributeValue( null ); - if ( mappingsValue instanceof PsiArrayInitializerMemberValue mappingsArrayInitializerMemberValue) { - return Stream.of( mappingsArrayInitializerMemberValue + PsiAnnotationMemberValue value = annotation.findDeclaredAttributeValue( null ); + if ( value instanceof PsiArrayInitializerMemberValue arrayInitializerMemberValue ) { + return Stream.of( arrayInitializerMemberValue .getInitializers() ) - .filter( MapstructAnnotationUtils::isMappingPsiAnnotation ) + .filter( PsiAnnotation.class::isInstance ) .map( PsiAnnotation.class::cast ); } - else if ( mappingsValue instanceof PsiAnnotation mappingsAnnotation ) { + else if ( value instanceof PsiAnnotation mappingsAnnotation ) { return Stream.of( mappingsAnnotation ); } return Stream.empty(); @@ -396,17 +396,6 @@ private static Stream findValueMappingAnnotations(@NotNull PsiMet .filter( MapstructAnnotationUtils::isValueMappingAnnotation ); } - /** - * @param memberValue that needs to be checked - * - * @return {@code true} if the {@code memberValue} is the {@link org.mapstruct.Mapping} {@link PsiAnnotation}, - * {@code false} otherwise - */ - private static boolean isMappingPsiAnnotation(PsiAnnotationMemberValue memberValue) { - return memberValue instanceof PsiAnnotation - && isMappingAnnotation( (PsiAnnotation) memberValue ); - } - /** * @param memberValue that needs to be checked * diff --git a/src/main/java/org/mapstruct/intellij/util/MapstructElementUtils.java b/src/main/java/org/mapstruct/intellij/util/MapstructElementUtils.java index 7d041c2..1e06785 100644 --- a/src/main/java/org/mapstruct/intellij/util/MapstructElementUtils.java +++ b/src/main/java/org/mapstruct/intellij/util/MapstructElementUtils.java @@ -42,6 +42,15 @@ public static PsiJavaElementPattern.Capture mappingElementPattern(St return elementPattern( parameterName, MapstructUtil.MAPPING_ANNOTATION_FQN ); } + /** + * @param parameterName the name of the parameter in the {@code @Ignored} annotation + * + * @return an element pattern for a parameter in the {@code @Ignored} annotation + */ + public static PsiJavaElementPattern.Capture ignoredElementPattern(String parameterName) { + return elementPattern( parameterName, MapstructUtil.IGNORED_ANNOTATION_FQN ); + } + /** * @param parameterName the name of the parameter in the {@code @Mapper} annotation * diff --git a/src/main/java/org/mapstruct/intellij/util/MapstructKotlinElementUtils.java b/src/main/java/org/mapstruct/intellij/util/MapstructKotlinElementUtils.java index 0fa680e..ce35208 100644 --- a/src/main/java/org/mapstruct/intellij/util/MapstructKotlinElementUtils.java +++ b/src/main/java/org/mapstruct/intellij/util/MapstructKotlinElementUtils.java @@ -62,6 +62,19 @@ public static KotlinElementPattern.Capture beanMappingElem ); } + /** + * @param parameterName the name of the parameter in the {@code @Ignored} annotation + * + * @return an element pattern for a parameter in the {@code @Ignored} annotation + */ + public static KotlinElementPattern.Capture ignoredElementPattern(String parameterName) { + return elementPattern( + parameterName, + MapstructUtil.IGNORED_ANNOTATION_FQN, + MapstructUtil.IGNORED_LIST_ANNOTATION_FQN + ); + } + private static KotlinElementPattern.Capture elementPattern(String parameterName, String annotationFQN, String annotationHolderFQN diff --git a/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java b/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java index b0ac84c..7d49019 100644 --- a/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java +++ b/src/main/java/org/mapstruct/intellij/util/MapstructUtil.java @@ -53,6 +53,8 @@ import org.mapstruct.Builder; import org.mapstruct.Context; import org.mapstruct.EnumMapping; +import org.mapstruct.Ignored; +import org.mapstruct.IgnoredList; import org.mapstruct.InheritConfiguration; import org.mapstruct.InheritInverseConfiguration; import org.mapstruct.Mapper; @@ -98,6 +100,9 @@ public class MapstructUtil { public static final String MAPPINGS_ANNOTATION_FQN = Mappings.class.getName(); + public static final String IGNORED_ANNOTATION_FQN = Ignored.class.getName(); + public static final String IGNORED_LIST_ANNOTATION_FQN = IgnoredList.class.getName(); + static final String VALUE_MAPPING_ANNOTATION_FQN = ValueMapping.class.getName(); static final String VALUE_MAPPINGS_ANNOTATION_FQN = ValueMappings.class.getName(); private static final String MAPPING_TARGET_ANNOTATION_FQN = MappingTarget.class.getName(); @@ -567,12 +572,16 @@ public static MapStructVersion resolveMapStructProjectVersion(@NotNull PsiFile p } return CachedValuesManager.getManager( module.getProject() ).getCachedValue( module, () -> { MapStructVersion mapStructVersion; - if ( JavaPsiFacade.getInstance( module.getProject() ) - .findClass( ENUM_MAPPING_ANNOTATION_FQN, module.getModuleRuntimeScope( false ) ) != null ) { + JavaPsiFacade javaPsiFacade = JavaPsiFacade.getInstance( module.getProject() ); + GlobalSearchScope moduleRuntimeScope = module.getModuleRuntimeScope( false ); + if ( javaPsiFacade.findClass( IGNORED_ANNOTATION_FQN, moduleRuntimeScope ) != null ) { + mapStructVersion = MapStructVersion.V1_7_O; + } + else if ( javaPsiFacade.findClass( ENUM_MAPPING_ANNOTATION_FQN, moduleRuntimeScope ) != null ) { mapStructVersion = MapStructVersion.V1_4_O; } - else if ( JavaPsiFacade.getInstance( module.getProject() ) - .findClass( BUILDER_ANNOTATION_FQN, module.getModuleRuntimeScope( false ) ) != null ) { + else if ( javaPsiFacade + .findClass( BUILDER_ANNOTATION_FQN, moduleRuntimeScope ) != null ) { mapStructVersion = MapStructVersion.V1_3_O; } else { diff --git a/src/main/java/org/mapstruct/intellij/util/TargetUtils.java b/src/main/java/org/mapstruct/intellij/util/TargetUtils.java index 886ca17..7fbb60d 100644 --- a/src/main/java/org/mapstruct/intellij/util/TargetUtils.java +++ b/src/main/java/org/mapstruct/intellij/util/TargetUtils.java @@ -430,6 +430,58 @@ public static Stream findAllDefinedMappingTargets(@NotNull PsiModifierLi .filter( s -> !s.isEmpty() ); } + /** + * Find all target properties that are ignored via {@link org.mapstruct.Ignored} annotations for the given method. + * This considers both directly placed {@code @Ignored} annotations and those wrapped in {@code @IgnoredList}, + * and prepends the {@code prefix} attribute value (if any) to each target. + * + * @param method that needs to be checked + * + * @return a stream of all ignored target property names (with prefix applied) + */ + @NotNull + public static Stream findAllIgnoredTargets(@NotNull PsiMethod method) { + Stream ignoredAnnotations = findIgnoredAnnotations( method ); + return ignoredAnnotations.flatMap( TargetUtils::extractIgnoredTargets ); + } + + @NotNull + private static Stream findIgnoredAnnotations(@NotNull PsiMethod method) { + PsiAnnotation ignoredList = AnnotationUtil.findAnnotation( + method, + true, + MapstructUtil.IGNORED_LIST_ANNOTATION_FQN + ); + + Stream fromList = MapstructAnnotationUtils.extractRepeatableAnnotations( ignoredList ); + + Stream direct = Stream.of( method.getModifierList().getAnnotations() ) + .filter( a -> MapstructUtil.IGNORED_ANNOTATION_FQN.equals( a.getQualifiedName() ) ); + + return Stream.concat( direct, fromList ); + } + + @NotNull + private static Stream extractIgnoredTargets(@NotNull PsiAnnotation ignoredAnnotation) { + + String prefixDot = getIgnoredPrefix( ignoredAnnotation ); + + return AnnotationUtil.arrayAttributeValues( ignoredAnnotation.findDeclaredAttributeValue( "targets" ) ) + .stream() + .map( AnnotationUtil::getStringAttributeValue ) + .filter( Objects::nonNull ) + .filter( s -> !s.isEmpty() ) + .map( target -> prefixDot + target ); + } + + public static String getIgnoredPrefix(@Nullable PsiAnnotation ignoredAnnotation) { + if ( ignoredAnnotation == null ) { + return ""; + } + String prefix = AnnotationUtil.getDeclaredStringAttributeValue( ignoredAnnotation, "prefix" ); + return ( prefix != null && !prefix.isEmpty() ) ? prefix + "." : ""; + } + /** * Find all implicit source properties for all targets mapping to the current target, i.e. ".". * diff --git a/src/main/java/org/mapstruct/intellij/util/patterns/KotlinElementPattern.java b/src/main/java/org/mapstruct/intellij/util/patterns/KotlinElementPattern.java index 9c2a111..8ac0426 100644 --- a/src/main/java/org/mapstruct/intellij/util/patterns/KotlinElementPattern.java +++ b/src/main/java/org/mapstruct/intellij/util/patterns/KotlinElementPattern.java @@ -37,12 +37,14 @@ public Self insideRepeatableAnnotationParam( KtValueArgumentPattern ktValueArgumentPattern = ktValueArgument().withName( parameterName ); return withElementType( KtStubElementTypes.STRING_TEMPLATE ).andOr( - withParent( + withAncestor( + 2, ktValueArgumentPattern .withAncestor( 5, ktAnnotation().qName( annotationHolderQualifiedName ) ) ), - withParent( + withAncestor( + 2, ktValueArgumentPattern .withSuperParent( 2, ktAnnotation().qName( annotationQualifiedName ) ) ) diff --git a/src/test/java/org/mapstruct/intellij/MapstructCompletionTestCase.java b/src/test/java/org/mapstruct/intellij/MapstructCompletionTestCase.java index 093b70d..7b6446a 100644 --- a/src/test/java/org/mapstruct/intellij/MapstructCompletionTestCase.java +++ b/src/test/java/org/mapstruct/intellij/MapstructCompletionTestCase.java @@ -271,6 +271,20 @@ public void testCarMapperReturnTargetCarDto() { assertCarDtoAutoComplete(); } + public void testCarMapperReturnTargetCarDtoWithIgnored() { + configureByTestName(); + assertThat( myItems ) + .extracting( LookupElement::getLookupString ) + .containsExactlyInAnyOrder( + "manufacturingYear", + "myDriver", + "passengers", + "price", + "category", + "available" + ); + } + public void testCarMapperReturnTargetFluentCarDto() { configureByTestName(); assertCarDtoAutoComplete(); diff --git a/src/test/java/org/mapstruct/intellij/completion/IgnoredTargetsCompletionTestCase.java b/src/test/java/org/mapstruct/intellij/completion/IgnoredTargetsCompletionTestCase.java new file mode 100644 index 0000000..bd14a1a --- /dev/null +++ b/src/test/java/org/mapstruct/intellij/completion/IgnoredTargetsCompletionTestCase.java @@ -0,0 +1,209 @@ +/* + * 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 com.intellij.psi.PsiElement; +import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiReference; +import org.mapstruct.intellij.MapstructBaseCompletionTestCase; + +import static org.assertj.core.api.Assertions.assertThat; + +/** + * @author Filip Hrisafov + */ +public class IgnoredTargetsCompletionTestCase extends MapstructBaseCompletionTestCase { + + @Override + protected String getTestDataPath() { + return "testData/completion/ignored"; + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + + addDirectoryToProject( "../../mapping/dto" ); + } + + private void assertCarDtoAutoComplete() { + assertThat( myItems ) + .extracting( LookupElement::getLookupString ) + .containsExactlyInAnyOrder( + "make", + "seatCount", + "manufacturingYear", + "myDriver", + "passengers", + "price", + "category", + "available" + ); + } + + public void testIgnoredTargetsNoPrefix() { + configureByTestName(); + assertCarDtoAutoComplete(); + } + + public void testIgnoredTargetsWithPrefix() { + configureByTestName(); + assertCarDtoAutoComplete(); + } + + public void testIgnoredTargetsMultipleTargets() { + configureByTestName(); + assertThat( myItems ) + .extracting( LookupElement::getLookupString ) + .containsExactlyInAnyOrder( + "manufacturingYear", + "myDriver", + "passengers", + "price", + "category", + "available" + ); + } + + public void testIgnoredTargetsWithPrefixMultipleTargets() { + configureByTestName(); + assertThat( myItems ) + .extracting( LookupElement::getLookupString ) + .containsExactlyInAnyOrder( + "seatCount", + "manufacturingYear", + "myDriver", + "passengers", + "price", + "category", + "available" + ); + } + + public void testIgnoredTargetsWithMapping() { + configureByTestName(); + assertThat( myItems ) + .extracting( LookupElement::getLookupString ) + .containsExactlyInAnyOrder( + "manufacturingYear", + "myDriver", + "passengers", + "price", + "category", + "available" + ); + } + + public void testIgnoredTargetsWithPrefixAndMapping() { + configureByTestName(); + assertThat( myItems ) + .extracting( LookupElement::getLookupString ) + .containsExactlyInAnyOrder( + "manufacturingYear", + "myDriver", + "passengers", + "price", + "category", + "available" + ); + } + + public void testIgnoredTargetsSingleNoArray() { + configureByTestName(); + assertCarDtoAutoComplete(); + } + + public void testIgnoredTargetsReferenceTarget() { + myFixture.configureByFile( "IgnoredTargetsReferenceTarget.java" ); + PsiElement reference = myFixture.getElementAtCaret(); + + assertThat( reference ) + .isInstanceOfSatisfying( PsiMethod.class, method -> { + assertThat( method.getName() ).isEqualTo( "setSeatCount" ); + } ); + } + + public void testIgnoredTargetsReferenceTargetWithPrefix() { + myFixture.configureByFile( "IgnoredTargetsReferenceTargetWithPrefix.java" ); + PsiElement reference = myFixture.getElementAtCaret(); + + assertThat( reference ) + .isInstanceOfSatisfying( PsiMethod.class, method -> { + assertThat( method.getName() ).isEqualTo( "setMake" ); + } ); + } + + public void testIgnoredTargetsReferenceUnknownTarget() { + myFixture.configureByFile( "IgnoredTargetsReferenceUnknownTarget.java" ); + PsiReference reference = myFixture.getFile().findReferenceAt( myFixture.getCaretOffset() ); + assertThat( reference ).isNotNull(); + assertThat( reference.resolve() ).isNull(); + } + + public void testIgnoredTargetsReferenceNestedTarget() { + myFixture.configureByFile( "IgnoredTargetsReferenceNestedTarget.java" ); + PsiElement reference = myFixture.getElementAtCaret(); + + assertThat( reference ) + .isInstanceOfSatisfying( PsiMethod.class, method -> { + assertThat( method.getName() ).isEqualTo( "setName" ); + } ); + } + + public void testIgnoredTargetsWithInvalidPrefix() { + myFixture.configureByFile( "IgnoredTargetsWithInvalidPrefix.java" ); + complete(); + assertThat( myItems ).isEmpty(); + } + + public void testIgnoredTargetsMultipleAnnotations() { + configureByTestName(); + assertThat( myItems ) + .extracting( LookupElement::getLookupString ) + .containsExactlyInAnyOrder( + "manufacturingYear", + "myDriver", + "passengers", + "price", + "category", + "available" + ); + } + + public void testIgnoredTargetsWithIgnoredList() { + configureByTestName(); + assertThat( myItems ) + .extracting( LookupElement::getLookupString ) + .containsExactlyInAnyOrder( + "manufacturingYear", + "myDriver", + "passengers", + "price", + "category", + "available" + ); + } + + public void testIgnoredTargetsNoPrefixKotlin() { + configureByFile( "/" + getTestName( false ) + ".kt" ); + assertCarDtoAutoComplete(); + } + + public void testIgnoredTargetsWithIgnoredListKotlin() { + configureByFile( "/" + getTestName( false ) + ".kt" ); + assertThat( myItems ) + .extracting( LookupElement::getLookupString ) + .containsExactlyInAnyOrder( + "manufacturingYear", + "myDriver", + "passengers", + "price", + "category", + "available" + ); + } +} diff --git a/src/test/java/org/mapstruct/intellij/inspection/MapstructReferenceInspectionTest.java b/src/test/java/org/mapstruct/intellij/inspection/MapstructReferenceInspectionTest.java index 9770355..0320985 100644 --- a/src/test/java/org/mapstruct/intellij/inspection/MapstructReferenceInspectionTest.java +++ b/src/test/java/org/mapstruct/intellij/inspection/MapstructReferenceInspectionTest.java @@ -49,4 +49,8 @@ public void testUnknownIgnoreUnmappedSourceReference() { public void testUnknownQualifiedByNameReferenceReference() { doTest(); } + + public void testUnknownIgnoredTargetReference() { + doTest(); + } } diff --git a/src/test/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspectionTest.java b/src/test/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspectionTest.java index 486f524..c7d13db 100644 --- a/src/test/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspectionTest.java +++ b/src/test/java/org/mapstruct/intellij/inspection/TargetPropertyMappedMoreThanOnceInspectionTest.java @@ -24,6 +24,14 @@ public class TargetPropertyMappedMoreThanOnceInspectionTest extends BaseInspecti return TargetPropertyMappedMoreThanOnceInspection.class; } + public void testTargetPropertyMappedMoreThanOnceWithIgnored() { + doTest(); + } + + public void testTargetPropertyMappedMoreThanOnceWithIgnoredList() { + doTest(); + } + public void testTargetPropertyMappedMoreThanOnce() { doTest(); String testName = getTestName( false ); diff --git a/src/test/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesWithIgnoredInspectionTest.java b/src/test/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesWithIgnoredInspectionTest.java new file mode 100644 index 0000000..41f6098 --- /dev/null +++ b/src/test/java/org/mapstruct/intellij/inspection/UnmappedTargetPropertiesWithIgnoredInspectionTest.java @@ -0,0 +1,47 @@ +/* + * 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 Filip Hrisafov + */ +public class UnmappedTargetPropertiesWithIgnoredInspectionTest 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" + ); + } + + public void testUnmappedTargetPropertiesWithIgnored() { + doTest(); + List allQuickFixes = myFixture.getAllQuickFixes(); + + assertThat( allQuickFixes ) + .extracting( IntentionAction::getText ) + .as( "Intent Text" ) + .containsExactly( + "Ignore unmapped target property: 'moreTarget'", + "Add unmapped target property: 'moreTarget'" + ); + } +} diff --git a/testData/completion/ignored/IgnoredTargetsMultipleAnnotations.java b/testData/completion/ignored/IgnoredTargetsMultipleAnnotations.java new file mode 100644 index 0000000..9bc1106 --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsMultipleAnnotations.java @@ -0,0 +1,19 @@ +/* + * 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.Ignored; +import org.mapstruct.Mapper; +import org.example.dto.Car; +import org.example.dto.CarDto; + +@Mapper +public interface IgnoredTargetsMultipleAnnotations { + + @Ignored(targets = { "make", "seatCount" }) + @Ignored(targets = { "" }) + CarDto carToCarDto(Car car); +} diff --git a/testData/completion/ignored/IgnoredTargetsMultipleTargets.java b/testData/completion/ignored/IgnoredTargetsMultipleTargets.java new file mode 100644 index 0000000..86abb91 --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsMultipleTargets.java @@ -0,0 +1,18 @@ +/* + * 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.Ignored; +import org.mapstruct.Mapper; +import org.example.dto.Car; +import org.example.dto.CarDto; + +@Mapper +public interface IgnoredTargetsMultipleTargets { + + @Ignored(targets = { "make", "seatCount", "" }) + CarDto carToCarDto(Car car); +} diff --git a/testData/completion/ignored/IgnoredTargetsNoPrefix.java b/testData/completion/ignored/IgnoredTargetsNoPrefix.java new file mode 100644 index 0000000..a831029 --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsNoPrefix.java @@ -0,0 +1,18 @@ +/* + * 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.Ignored; +import org.mapstruct.Mapper; +import org.example.dto.Car; +import org.example.dto.CarDto; + +@Mapper +public interface IgnoredTargetsNoPrefix { + + @Ignored(targets = { "" }) + CarDto carToCarDto(Car car); +} diff --git a/testData/completion/ignored/IgnoredTargetsNoPrefixKotlin.kt b/testData/completion/ignored/IgnoredTargetsNoPrefixKotlin.kt new file mode 100644 index 0000000..ed4377e --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsNoPrefixKotlin.kt @@ -0,0 +1,18 @@ +/** + * 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.Ignored +import org.mapstruct.Mapper +import org.example.dto.Car +import org.example.dto.CarDtoKt + +@Mapper +interface IgnoredTargetsNoPrefixKotlin { + + @Ignored(targets = [""]) + fun carToCarDto(car: Car): CarDtoKt +} diff --git a/testData/completion/ignored/IgnoredTargetsReferenceNestedTarget.java b/testData/completion/ignored/IgnoredTargetsReferenceNestedTarget.java new file mode 100644 index 0000000..71bc8ea --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsReferenceNestedTarget.java @@ -0,0 +1,18 @@ +/* + * 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.Ignored; +import org.mapstruct.Mapper; +import org.example.dto.Car; +import org.example.dto.CarDto; + +@Mapper +public interface IgnoredTargetsReferenceNestedTarget { + + @Ignored(targets = { "myDriver.name" }) + CarDto carToCarDto(Car car); +} diff --git a/testData/completion/ignored/IgnoredTargetsReferenceTarget.java b/testData/completion/ignored/IgnoredTargetsReferenceTarget.java new file mode 100644 index 0000000..b60cccf --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsReferenceTarget.java @@ -0,0 +1,18 @@ +/* + * 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.Ignored; +import org.mapstruct.Mapper; +import org.example.dto.Car; +import org.example.dto.CarDto; + +@Mapper +public interface IgnoredTargetsReferenceTarget { + + @Ignored(targets = { "seatCount" }) + CarDto carToCarDto(Car car); +} diff --git a/testData/completion/ignored/IgnoredTargetsReferenceTargetWithPrefix.java b/testData/completion/ignored/IgnoredTargetsReferenceTargetWithPrefix.java new file mode 100644 index 0000000..24bc1b6 --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsReferenceTargetWithPrefix.java @@ -0,0 +1,18 @@ +/* + * 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.Ignored; +import org.mapstruct.Mapper; +import org.example.dto.Garage; +import org.example.dto.GarageDto; + +@Mapper +public interface IgnoredTargetsReferenceTargetWithPrefix { + + @Ignored(prefix = "car", targets = { "make" }) + GarageDto garageToGarageDto(Garage garage); +} diff --git a/testData/completion/ignored/IgnoredTargetsReferenceUnknownTarget.java b/testData/completion/ignored/IgnoredTargetsReferenceUnknownTarget.java new file mode 100644 index 0000000..d8e91a7 --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsReferenceUnknownTarget.java @@ -0,0 +1,18 @@ +/* + * 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.Ignored; +import org.mapstruct.Mapper; +import org.example.dto.Car; +import org.example.dto.CarDto; + +@Mapper +public interface IgnoredTargetsReferenceUnknownTarget { + + @Ignored(targets = { "unknown" }) + CarDto carToCarDto(Car car); +} diff --git a/testData/completion/ignored/IgnoredTargetsSingleNoArray.java b/testData/completion/ignored/IgnoredTargetsSingleNoArray.java new file mode 100644 index 0000000..22ccaa6 --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsSingleNoArray.java @@ -0,0 +1,18 @@ +/* + * 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.Ignored; +import org.mapstruct.Mapper; +import org.example.dto.Car; +import org.example.dto.CarDto; + +@Mapper +public interface IgnoredTargetsSingleNoArray { + + @Ignored(targets = "") + CarDto carToCarDto(Car car); +} diff --git a/testData/completion/ignored/IgnoredTargetsWithIgnoredList.java b/testData/completion/ignored/IgnoredTargetsWithIgnoredList.java new file mode 100644 index 0000000..ae5fb88 --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsWithIgnoredList.java @@ -0,0 +1,22 @@ +/* + * 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.Ignored; +import org.mapstruct.IgnoredList; +import org.mapstruct.Mapper; +import org.example.dto.Car; +import org.example.dto.CarDto; + +@Mapper +public interface IgnoredTargetsWithIgnoredList { + + @IgnoredList({ + @Ignored(targets = { "make", "seatCount" }), + @Ignored(targets = { "" }) + }) + CarDto carToCarDto(Car car); +} diff --git a/testData/completion/ignored/IgnoredTargetsWithIgnoredListKotlin.kt b/testData/completion/ignored/IgnoredTargetsWithIgnoredListKotlin.kt new file mode 100644 index 0000000..2281f47 --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsWithIgnoredListKotlin.kt @@ -0,0 +1,22 @@ +/** + * 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.Ignored +import org.mapstruct.IgnoredList +import org.mapstruct.Mapper +import org.example.dto.Car +import org.example.dto.CarDtoKt + +@Mapper +interface IgnoredTargetsWithIgnoredListKotlin { + + @IgnoredList( + Ignored(targets = ["make", "seatCount"]), + Ignored(targets = [""]) + ) + fun carToCarDto(car: Car): CarDtoKt +} diff --git a/testData/completion/ignored/IgnoredTargetsWithInvalidPrefix.java b/testData/completion/ignored/IgnoredTargetsWithInvalidPrefix.java new file mode 100644 index 0000000..d804225 --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsWithInvalidPrefix.java @@ -0,0 +1,18 @@ +/* + * 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.Ignored; +import org.mapstruct.Mapper; +import org.example.dto.Garage; +import org.example.dto.GarageDto; + +@Mapper +public interface IgnoredTargetsWithInvalidPrefix { + + @Ignored(prefix = "nonExistent", targets = { "" }) + GarageDto garageToGarageDto(Garage garage); +} diff --git a/testData/completion/ignored/IgnoredTargetsWithMapping.java b/testData/completion/ignored/IgnoredTargetsWithMapping.java new file mode 100644 index 0000000..f6e60f9 --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsWithMapping.java @@ -0,0 +1,20 @@ +/* + * 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.Ignored; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.example.dto.Car; +import org.example.dto.CarDto; + +@Mapper +public interface IgnoredTargetsWithMapping { + + @Ignored(targets = { "seatCount", "" }) + @Mapping(target = "make", ignore = true) + CarDto carToCarDto(Car car); +} diff --git a/testData/completion/ignored/IgnoredTargetsWithPrefix.java b/testData/completion/ignored/IgnoredTargetsWithPrefix.java new file mode 100644 index 0000000..c65389a --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsWithPrefix.java @@ -0,0 +1,18 @@ +/* + * 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.Ignored; +import org.mapstruct.Mapper; +import org.example.dto.Garage; +import org.example.dto.GarageDto; + +@Mapper +public interface IgnoredTargetsWithPrefix { + + @Ignored(prefix = "car", targets = { "" }) + GarageDto garageToGarageDto(Garage garage); +} diff --git a/testData/completion/ignored/IgnoredTargetsWithPrefixAndMapping.java b/testData/completion/ignored/IgnoredTargetsWithPrefixAndMapping.java new file mode 100644 index 0000000..30fb96b --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsWithPrefixAndMapping.java @@ -0,0 +1,20 @@ +/* + * 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.Ignored; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.example.dto.Garage; +import org.example.dto.GarageDto; + +@Mapper +public interface IgnoredTargetsWithPrefixAndMapping { + + @Ignored(prefix = "car", targets = { "seatCount", "" }) + @Mapping(target = "car.make", source = "car.make") + GarageDto garageToGarageDto(Garage garage); +} diff --git a/testData/completion/ignored/IgnoredTargetsWithPrefixMultipleTargets.java b/testData/completion/ignored/IgnoredTargetsWithPrefixMultipleTargets.java new file mode 100644 index 0000000..5e6a637 --- /dev/null +++ b/testData/completion/ignored/IgnoredTargetsWithPrefixMultipleTargets.java @@ -0,0 +1,18 @@ +/* + * 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.Ignored; +import org.mapstruct.Mapper; +import org.example.dto.Garage; +import org.example.dto.GarageDto; + +@Mapper +public interface IgnoredTargetsWithPrefixMultipleTargets { + + @Ignored(prefix = "car", targets = { "make", "" }) + GarageDto garageToGarageDto(Garage garage); +} diff --git a/testData/inspection/TargetPropertyMappedMoreThanOnceWithIgnored.java b/testData/inspection/TargetPropertyMappedMoreThanOnceWithIgnored.java new file mode 100644 index 0000000..7c2ba0f --- /dev/null +++ b/testData/inspection/TargetPropertyMappedMoreThanOnceWithIgnored.java @@ -0,0 +1,102 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.mapstruct.Ignored; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +class Source { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} + +class Target { + private String testName; + + public String getTestName() { + return testName; + } + + public void setTestName(String testName) { + this.testName = testName; + } +} + +class Inner { + + private Target target; + + public Target getTarget() { + return target; + } + + public void setTarget(Target target) { + this.target = target; + } +} + +@Mapper +interface MappingAndIgnoredSameTargetMapper { + + @Mapping(target = "testName", source = "name") + @Ignored(targets = { "testName" }) + Target map(Source source); +} + +@Mapper +interface IgnoredAndIgnoredSameTargetMapper { + + @Ignored(targets = { "testName" }) + @Ignored(targets = { "testName" }) + Target map(Source source); +} + +@Mapper +interface IgnoredWithPrefixAndMappingSameTargetMapper { + + @Mapping(target = "target.testName", source = "name") + @Ignored(prefix = "target", targets = { "testName" }) + Inner map(Source source); +} + +@Mapper +interface IgnoredNoConflictMapper { + + @Mapping(target = "testName", source = "name") + @Ignored(targets = { "other" }) + Target map(Source source); +} + +@Mapper +interface IgnoredWithPrefixNoConflictMapper { + + @Mapping(target = "testName", source = "name") + @Ignored(prefix = "target", targets = { "testName" }) + Inner map(Source source); +} + +@Mapper +interface IgnoredSingleValueAndMappingMapper { + + @Mapping(target = "testName", source = "name") + @Ignored(targets = "testName") + Target map(Source source); +} + +@Mapper +interface MultipleTargetsInIgnoredMapper { + + @Mapping(target = "testName", source = "name") + @Ignored(targets = { "testName", "other" }) + Target map(Source source); +} diff --git a/testData/inspection/TargetPropertyMappedMoreThanOnceWithIgnoredList.java b/testData/inspection/TargetPropertyMappedMoreThanOnceWithIgnoredList.java new file mode 100644 index 0000000..fd51de2 --- /dev/null +++ b/testData/inspection/TargetPropertyMappedMoreThanOnceWithIgnoredList.java @@ -0,0 +1,73 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.mapstruct.Ignored; +import org.mapstruct.IgnoredList; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +class Source { + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} + +class Target { + private String testName; + private String moreTarget; + + public String getTestName() { + return testName; + } + + public void setTestName(String testName) { + this.testName = testName; + } + + public String getMoreTarget() { + return moreTarget; + } + + public void setMoreTarget(String moreTarget) { + this.moreTarget = moreTarget; + } +} + +@Mapper +interface MappingAndIgnoredListSameTargetMapper { + + @Mapping(target = "testName", source = "name") + @IgnoredList({ + @Ignored(targets = { "testName" }) + }) + Target map(Source source); +} + +@Mapper +interface IgnoredListDuplicateTargetMapper { + + @IgnoredList({ + @Ignored(targets = { "testName" }), + @Ignored(targets = { "testName" }) + }) + Target map(Source source); +} + +@Mapper +interface IgnoredListNoConflictMapper { + + @IgnoredList({ + @Ignored(targets = { "testName" }), + @Ignored(targets = { "moreTarget" }) + }) + Target map(Source source); +} diff --git a/testData/inspection/UnknownIgnoredTargetReference.java b/testData/inspection/UnknownIgnoredTargetReference.java new file mode 100644 index 0000000..20ca865 --- /dev/null +++ b/testData/inspection/UnknownIgnoredTargetReference.java @@ -0,0 +1,117 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0 + */ + +import org.mapstruct.Ignored; +import org.mapstruct.Mapper; + +class Source { + + private String name; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} + +class Target { + + private String testName; + + public String getTestName() { + return testName; + } + + public void setTestName(String testName) { + this.testName = testName; + } +} + +class Inner { + + private Target target; + + public Target getTarget() { + return target; + } + + public void setTarget(Target target) { + this.target = target; + } +} + +@Mapper +interface SingleIgnoredMapper { + + @Ignored(targets = { "name" }) + Target map(Source source); +} + +@Mapper +interface ValidIgnoredMapper { + + @Ignored(targets = { "testName" }) + Target map(Source source); +} + +@Mapper +interface EmptyIgnoredMapper { + + @Ignored(targets = { "" }) + Target map(Source source); +} + +@Mapper +interface MultipleIgnoredMapper { + + @Ignored(targets = { "testName", "unknown" }) + Target map(Source source); +} + +@Mapper +interface IgnoredWithPrefixMapper { + + @Ignored(prefix = "target", targets = { "unknown" }) + Inner map(Source source); +} + +@Mapper +interface ValidIgnoredWithPrefixMapper { + + @Ignored(prefix = "target", targets = { "testName" }) + Inner map(Source source); +} + +@Mapper +interface IgnoredNestedTargetMapper { + + @Ignored(targets = { "target.unknown" }) + Inner map(Source source); +} + +@Mapper +interface ValidIgnoredNestedTargetMapper { + + @Ignored(targets = { "target.testName" }) + Inner map(Source source); +} + +@Mapper +interface SingleNoArrayIgnoredMapper { + + @Ignored(targets = "name") + Target map(Source source); +} + +@Mapper +interface IgnoredWithInvalidPrefixMapper { + + @Ignored(prefix = "nonExistent", targets = { "foo" }) + Inner map(Source source); +} diff --git a/testData/inspection/UnmappedTargetPropertiesWithIgnored.java b/testData/inspection/UnmappedTargetPropertiesWithIgnored.java new file mode 100644 index 0000000..afa9104 --- /dev/null +++ b/testData/inspection/UnmappedTargetPropertiesWithIgnored.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.Ignored; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.example.data.UnmappedTargetPropertiesData.Target; +import org.example.data.UnmappedTargetPropertiesData.Source; +import org.example.data.UnmappedTargetPropertiesData.TargetWithInnerObject; + +@Mapper +interface AllIgnoredMapper { + + @Ignored(targets = { "testName", "moreTarget" }) + Target map(Source source); +} + +@Mapper +interface PartiallyIgnoredMapper { + + @Ignored(targets = { "testName" }) + Target map(Source source); +} + +@Mapper +interface IgnoredAndMappedMapper { + + @Ignored(targets = { "moreTarget" }) + @Mapping(target = "testName", source = "name") + Target map(Source source); +} + +@Mapper +interface IgnoredWithPrefixMapper { + + @Ignored(prefix = "testTarget", targets = { "testName", "moreTarget" }) + TargetWithInnerObject map(Source source); +} + +@Mapper +interface MultipleIgnoredAnnotationsMapper { + + @Ignored(targets = { "testName" }) + @Ignored(targets = { "moreTarget" }) + Target map(Source source); +} + +@Mapper +interface IgnoredWithPrefixAndMappingMapper { + + @Ignored(prefix = "testTarget", targets = { "moreTarget" }) + @Mapping(target = "testTarget.testName", source = "name") + TargetWithInnerObject map(Source source); +} diff --git a/testData/mapping/CarMapperReturnTargetCarDtoWithIgnored.java b/testData/mapping/CarMapperReturnTargetCarDtoWithIgnored.java new file mode 100644 index 0000000..13ff967 --- /dev/null +++ b/testData/mapping/CarMapperReturnTargetCarDtoWithIgnored.java @@ -0,0 +1,20 @@ +/* + * 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.Ignored; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.example.dto.CarDto; +import org.example.dto.Car; + +@Mapper +public interface CarMapperReturnTargetCarDtoWithIgnored { + + @Ignored(targets = { "seatCount", "make" }) + @Mapping(source = "manufacturingDate", target = "") + CarDto carToCarDto(Car car); +}