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 dbe6ff64..d002f2a2 100644 --- a/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructMappingQualifiedByNameReference.java +++ b/src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructMappingQualifiedByNameReference.java @@ -18,6 +18,7 @@ import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiMethod; +import com.intellij.psi.PsiModifier; import com.intellij.psi.PsiParameter; import com.intellij.psi.PsiReference; import com.intellij.psi.PsiType; @@ -110,15 +111,41 @@ private Stream findAllNamedMethodsFromThisAndReferencedMappers(@NotNu return Stream.empty(); } - Stream internalMethods = Stream.of( containingClass.getMethods() ) - .filter( MapstructUtil::isNamedMethod ); + Stream internalMethods = Stream.of( containingClass.getAllMethods() ) + .filter( MapstructUtil::isNamedMethod ) + .filter( m -> !m.hasModifierProperty( PsiModifier.PRIVATE ) ); - Stream externalMethods = findNamedMethodsInUsedMappers( containingClass ); + Stream externalMethods = findNamedMethodsInUsedMappers( containingClass ) + .filter( method -> methodIsAccessibleFrom( method, containingClass ) ); return Stream.concat( internalMethods, externalMethods ) .filter( this::methodHasReturnType ); } + private boolean methodIsAccessibleFrom(PsiMethod method, PsiClass containingClass) { + PsiClass methodClass = method.getContainingClass(); + if ( methodClass == null ) { + return false; + } + + if ( method.hasModifierProperty( PsiModifier.PRIVATE ) ) { + return false; + } + + if ( method.hasModifierProperty( PsiModifier.PUBLIC ) ) { + return true; + } + + return haveSamePackage( containingClass, methodClass ); + } + + private boolean haveSamePackage(PsiClass firstClass, PsiClass secondClass) { + return Objects.equals( + StringUtil.getPackageName( Objects.requireNonNull( firstClass.getQualifiedName() ) ), + StringUtil.getPackageName( Objects.requireNonNull( secondClass.getQualifiedName() ) ) + ); + } + @NotNull private Stream findNamedMethodsInUsedMappers(@Nullable PsiClass containingClass) { diff --git a/src/test/java/org/mapstruct/intellij/completion/MappingQualifiedByNameCompletionTestCase.java b/src/test/java/org/mapstruct/intellij/completion/MappingQualifiedByNameCompletionTestCase.java index 5c8de1f0..c4704084 100644 --- a/src/test/java/org/mapstruct/intellij/completion/MappingQualifiedByNameCompletionTestCase.java +++ b/src/test/java/org/mapstruct/intellij/completion/MappingQualifiedByNameCompletionTestCase.java @@ -83,10 +83,77 @@ private void assertQualifiedByNameInsideMapperConfigAutoComplete() { assertThat( myItems ) .extracting( LookupElementPresentation::renderElement ) .usingRecursiveFieldByFieldElementComparator() + .describedAs( "methods of mappers from @MapperConfig(uses = ...)" ) .containsOnly( - // methods of mappers from @MapperConfig(uses = ...) createMethod( "unwrapOptional", "T", " OptionalMapper#unwrapOptional(Optional)" ) ); } + public void testMappingQualifiedByNameWithAllPossibleVisibilities() { + configureByTestName(); + assertAutoCompleteOfValidVisibilities(); + } + + private void assertAutoCompleteOfValidVisibilities() { + + assertThat( myItems ) + .extracting( LookupElement::getLookupString ) + .containsExactlyInAnyOrder( + "internalModifierPackagePrivate", + "internalModifierProtected", + "internalModifierPublic", + "superClassModifierPackagePrivate", + "superClassModifierProtected", + "superClassModifierPublic", + "samePackageModifierPackagePrivate", + "samePackageModifierProtected", + "samePackageModifierPublic", + "externalPackageModifierPublic" + ); + + assertThat( myItems ) + .extracting( LookupElementPresentation::renderElement ) + .usingRecursiveFieldByFieldElementComparator() + .containsExactlyInAnyOrder( + createMethod( + "internalModifierPackagePrivate", + "String", + " CarMapper#internalModifierPackagePrivate(String)" + ), + createMethod( "internalModifierProtected", "String", " CarMapper#internalModifierProtected(String)" ), + createMethod( "internalModifierPublic", "String", " CarMapper#internalModifierPublic(String)" ), + createMethod( + "superClassModifierPackagePrivate", + "String", + " BaseMapper#superClassModifierPackagePrivate(String)" + ), + createMethod( + "superClassModifierProtected", + "String", + " BaseMapper#superClassModifierProtected(String)" + ), + createMethod( "superClassModifierPublic", "String", " BaseMapper#superClassModifierPublic(String)" ), + createMethod( + "samePackageModifierPackagePrivate", + "String", + " SamePackageMapper#samePackageModifierPackagePrivate(String)" + ), + createMethod( + "samePackageModifierProtected", + "String", + " SamePackageMapper#samePackageModifierProtected(String)" + ), + createMethod( + "samePackageModifierPublic", + "String", + " SamePackageMapper#samePackageModifierPublic(String)" + ), + createMethod( + "externalPackageModifierPublic", + "String", + " ExternalMapper#externalPackageModifierPublic(String)" + ) + ); + } + } diff --git a/testData/completion/qualifiedbyname/MappingQualifiedByNameWithAllPossibleVisibilities.java b/testData/completion/qualifiedbyname/MappingQualifiedByNameWithAllPossibleVisibilities.java new file mode 100644 index 00000000..298f6cf8 --- /dev/null +++ b/testData/completion/qualifiedbyname/MappingQualifiedByNameWithAllPossibleVisibilities.java @@ -0,0 +1,43 @@ +/* + * 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.example.dto.Car; +import org.example.dto.CarDto; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import org.mapstruct.ap.test.complex.BaseMapper; +import org.mapstruct.ap.test.complex.SamePackageMapper; +import org.mapstruct.helper.qualifiedbyname.external.ExternalMapper; + +@Mapper(uses = { ExternalMapper.class, SamePackageMapper.class }) +public abstract class CarMapper extends BaseMapper { + + @Mapping(target = "make", qualifiedByName = "") + public abstract CarDto carToCarDto(Car car); + + @Named("internalModifierPackagePrivate") + String internalModifierPackagePrivate(String value) { + return ""; + } + + @Named("internalModifierPrivate") + private String internalModifierPrivate(String value) { + return ""; + } + + @Named("internalModifierProtected") + protected String internalModifierProtected(String value) { + return ""; + } + + @Named("internalModifierPublic") + public String internalModifierPublic(String value) { + return ""; + } + +} diff --git a/testData/completion/qualifiedbyname/mapper/BaseMapper.java b/testData/completion/qualifiedbyname/mapper/BaseMapper.java new file mode 100644 index 00000000..e5424a62 --- /dev/null +++ b/testData/completion/qualifiedbyname/mapper/BaseMapper.java @@ -0,0 +1,34 @@ +/* + * 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.Mapper; +import org.mapstruct.Named; + +@Mapper +public abstract class BaseMapper { + + @Named("superClassModifierPackagePrivate") + String superClassModifierPackagePrivate(String value) { + return ""; + } + + @Named("superClassModifierPrivate") + private String superClassModifierPrivate(String value) { + return ""; + } + + @Named("superClassModifierProtected") + protected String superClassModifierProtected(String value) { + return ""; + } + + @Named("superClassModifierPublic") + public String superClassModifierPublic(String value) { + return ""; + } + +} diff --git a/testData/completion/qualifiedbyname/mapper/ExternalMapper.java b/testData/completion/qualifiedbyname/mapper/ExternalMapper.java new file mode 100644 index 00000000..3dae73d4 --- /dev/null +++ b/testData/completion/qualifiedbyname/mapper/ExternalMapper.java @@ -0,0 +1,34 @@ +/* + * Copyright MapStruct Authors. + * + * Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0 + */ +package org.mapstruct.helper.qualifiedbyname.external; + +import org.mapstruct.Mapper; +import org.mapstruct.Named; + +@Mapper +public abstract class ExternalMapper { + + @Named("externalPackageModifierPackagePrivate") + String externalPackageModifierPackagePrivate(String value) { + return ""; + } + + @Named("externalPackageModifierPrivate") + private String externalPackageModifierPrivate(String value) { + return ""; + } + + @Named("externalPackageModifierProtected") + protected String externalPackageModifierProtected(String value) { + return ""; + } + + @Named("externalPackageModifierPublic") + public String externalPackageModifierPublic(String value) { + return ""; + } + +} diff --git a/testData/completion/qualifiedbyname/mapper/SamePackageMapper.java b/testData/completion/qualifiedbyname/mapper/SamePackageMapper.java new file mode 100644 index 00000000..a9cf6d5f --- /dev/null +++ b/testData/completion/qualifiedbyname/mapper/SamePackageMapper.java @@ -0,0 +1,34 @@ +/* + * 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.Mapper; +import org.mapstruct.Named; + +@Mapper +public abstract class SamePackageMapper { + + @Named("samePackageModifierPackagePrivate") + String samePackageModifierPackagePrivate(String value) { + return ""; + } + + @Named("samePackageModifierPrivate") + private String samePackageModifierPrivate(String value) { + return ""; + } + + @Named("samePackageModifierProtected") + protected String samePackageModifierProtected(String value) { + return ""; + } + + @Named("samePackageModifierPublic") + public String samePackageModifierPublic(String value) { + return ""; + } + +}