Skip to content

Commit 6660811

Browse files
thunderhookfiliphr
authored andcommitted
#16 Add Auto-completion support for public fields
1 parent dc41956 commit 6660811

14 files changed

Lines changed: 385 additions & 12 deletions

src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructSourceReference.java

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@
66
package org.mapstruct.intellij.codeinsight.references;
77

88
import java.util.Objects;
9+
import java.util.Set;
10+
import java.util.stream.Collectors;
911
import java.util.stream.Stream;
1012

1113
import com.intellij.codeInsight.lookup.LookupElement;
1214
import com.intellij.openapi.util.TextRange;
1315
import com.intellij.psi.PsiClass;
1416
import com.intellij.psi.PsiElement;
17+
import com.intellij.psi.PsiField;
1518
import com.intellij.psi.PsiLiteral;
1619
import com.intellij.psi.PsiMethod;
1720
import com.intellij.psi.PsiParameter;
@@ -22,6 +25,8 @@
2225
import org.jetbrains.annotations.Nullable;
2326
import org.mapstruct.intellij.util.MapstructUtil;
2427

28+
import static org.mapstruct.intellij.util.MapstructUtil.asLookup;
29+
import static org.mapstruct.intellij.util.MapstructUtil.isPublic;
2530
import static org.mapstruct.intellij.util.SourceUtils.getParameterType;
2631
import static org.mapstruct.intellij.util.SourceUtils.publicGetters;
2732

@@ -55,7 +60,15 @@ PsiElement resolveInternal(@NotNull String value, @NotNull PsiType psiType) {
5560
if ( methods.length == 0 ) {
5661
methods = psiClass.findMethodsByName( "is" + MapstructUtil.capitalize( value ), true );
5762
}
58-
return methods.length == 0 ? null : methods[0];
63+
if ( methods.length > 0 ) {
64+
return methods[0];
65+
}
66+
67+
PsiField field = psiClass.findFieldByName( value, true );
68+
if ( field != null && isPublic( field ) ) {
69+
return field;
70+
}
71+
return null;
5972
}
6073

6174
@Override
@@ -83,9 +96,20 @@ PsiElement resolveInternal(@NotNull String value, @NotNull PsiMethod mappingMeth
8396
@NotNull
8497
@Override
8598
Object[] getVariantsInternal(@NotNull PsiType psiType) {
86-
return publicGetters( psiType )
87-
.map( pair -> MapstructUtil.asLookup( pair, PsiMethod::getReturnType ) )
88-
.toArray();
99+
Set<LookupElement> elements = publicGetters( psiType )
100+
.map( pair -> MapstructUtil.asLookup( pair, PsiMethod::getReturnType ) )
101+
.collect( Collectors.toSet() );
102+
103+
PsiClass psiClass = PsiUtil.resolveClassInType( psiType );
104+
if ( psiClass != null ) {
105+
for ( PsiField field : psiClass.getAllFields() ) {
106+
if ( isPublic( field ) ) {
107+
elements.add( asLookup( field ) );
108+
}
109+
}
110+
}
111+
112+
return elements.toArray();
89113
}
90114

91115
@NotNull

src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructTargetReference.java

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,31 @@
66
package org.mapstruct.intellij.codeinsight.references;
77

88
import java.util.Objects;
9+
import java.util.Set;
10+
import java.util.stream.Collectors;
911
import java.util.stream.Stream;
1012

1113
import com.intellij.codeInsight.lookup.LookupElement;
1214
import com.intellij.openapi.util.Pair;
1315
import com.intellij.openapi.util.TextRange;
1416
import com.intellij.psi.PsiClass;
1517
import com.intellij.psi.PsiElement;
18+
import com.intellij.psi.PsiField;
1619
import com.intellij.psi.PsiLiteral;
1720
import com.intellij.psi.PsiMethod;
1821
import com.intellij.psi.PsiParameter;
1922
import com.intellij.psi.PsiReference;
2023
import com.intellij.psi.PsiType;
24+
import com.intellij.psi.util.PsiUtil;
2125
import org.jetbrains.annotations.NotNull;
2226
import org.jetbrains.annotations.Nullable;
2327
import org.mapstruct.intellij.util.MapstructUtil;
24-
import org.mapstruct.intellij.util.TargetUtils;
2528

29+
import static org.mapstruct.intellij.util.MapstructUtil.asLookup;
30+
import static org.mapstruct.intellij.util.MapstructUtil.isPublicModifiable;
2631
import static org.mapstruct.intellij.util.TargetUtils.getRelevantType;
2732
import static org.mapstruct.intellij.util.TargetUtils.publicSetters;
33+
import static org.mapstruct.intellij.util.TargetUtils.resolveBuilderOrSelfClass;
2834

2935
/**
3036
* Reference for {@link org.mapstruct.Mapping#target()}.
@@ -51,7 +57,7 @@ private MapstructTargetReference(PsiLiteral element, MapstructTargetReference pr
5157

5258
@Override
5359
PsiElement resolveInternal(@NotNull String value, @NotNull PsiType psiType) {
54-
Pair<PsiClass, PsiType> pair = TargetUtils.resolveBuilderOrSelfClass( psiType, builderSupportPresent );
60+
Pair<PsiClass, PsiType> pair = resolveBuilderOrSelfClass( psiType, builderSupportPresent );
5561
if ( pair == null ) {
5662
return null;
5763
}
@@ -73,6 +79,14 @@ PsiElement resolveInternal(@NotNull String value, @NotNull PsiType psiType) {
7379
}
7480
}
7581

82+
PsiClass selfClass = PsiUtil.resolveClassInType( psiType );
83+
if ( selfClass != null ) {
84+
PsiField field = selfClass.findFieldByName( value, true );
85+
if ( field != null && isPublicModifiable( field ) ) {
86+
return field;
87+
}
88+
}
89+
7690
return null;
7791
}
7892

@@ -98,12 +112,23 @@ PsiElement resolveInternal(@NotNull String value, @NotNull PsiMethod mappingMeth
98112
@NotNull
99113
@Override
100114
Object[] getVariantsInternal(@NotNull PsiType psiType) {
101-
return publicSetters( psiType, builderSupportPresent )
102-
.map( pair -> MapstructUtil.asLookup(
103-
pair,
104-
MapstructTargetReference::firstParameterPsiType
105-
) )
106-
.toArray();
115+
116+
Set<LookupElement> elements = publicSetters( psiType, builderSupportPresent )
117+
.map( pair -> asLookup(
118+
pair,
119+
MapstructTargetReference::firstParameterPsiType
120+
) ).collect( Collectors.toSet() );
121+
122+
PsiClass psiClass = PsiUtil.resolveClassInType( psiType );
123+
if ( psiClass != null ) {
124+
for ( PsiField field : psiClass.getAllFields() ) {
125+
if ( isPublicModifiable( field ) ) {
126+
elements.add( asLookup( field ) );
127+
}
128+
}
129+
}
130+
131+
return elements.toArray();
107132
}
108133

109134
@NotNull

src/main/java/org/mapstruct/intellij/util/MapstructUtil.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.intellij.psi.PsiArrayType;
2121
import com.intellij.psi.PsiClass;
2222
import com.intellij.psi.PsiClassType;
23+
import com.intellij.psi.PsiField;
2324
import com.intellij.psi.PsiFile;
2425
import com.intellij.psi.PsiMethod;
2526
import com.intellij.psi.PsiModifier;
@@ -105,6 +106,13 @@ public static LookupElement asLookup(@NotNull Pair<PsiMethod, PsiSubstitutor> pa
105106
return builder;
106107
}
107108

109+
public static LookupElement asLookup(@NotNull PsiField field) {
110+
return LookupElementBuilder.create( field )
111+
.withIcon( PlatformIcons.FIELD_ICON )
112+
.withPresentableText( field.getNameIdentifier().getText() )
113+
.withTypeText( field.getType().getPresentableText() );
114+
}
115+
108116
public static boolean isPublic(@NotNull PsiMethod method) {
109117
return method.hasModifierProperty( PsiModifier.PUBLIC );
110118
}
@@ -113,6 +121,16 @@ public static boolean isPublicStatic(@NotNull PsiMethod method) {
113121
return isPublic( method ) && method.hasModifierProperty( PsiModifier.STATIC );
114122
}
115123

124+
public static boolean isPublic(@NotNull PsiField field) {
125+
return field.hasModifierProperty( PsiModifier.PUBLIC );
126+
}
127+
128+
public static boolean isPublicModifiable(@NotNull PsiField field) {
129+
return isPublic( field ) &&
130+
!field.hasModifierProperty( PsiModifier.FINAL ) &&
131+
!field.hasModifierProperty( PsiModifier.STATIC );
132+
}
133+
116134
public static boolean isSetter(@NotNull PsiMethod method) {
117135
if ( method.getParameterList().getParametersCount() != 1 ) {
118136
return false;

src/test/java/org/mapstruct/intellij/MapstructCompletionTestCase.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import com.intellij.codeInsight.lookup.LookupElementPresentation;
1010
import com.intellij.psi.PsiClass;
1111
import com.intellij.psi.PsiElement;
12+
import com.intellij.psi.PsiField;
1213
import com.intellij.psi.PsiMethod;
1314
import com.intellij.psi.PsiParameter;
1415
import com.intellij.psi.PsiReference;
@@ -93,6 +94,35 @@ private void assertCarDtoWithBuilderAutoComplete() {
9394
);
9495
}
9596

97+
private void assertCarDtoPublicAutoComplete() {
98+
assertThat( myItems )
99+
.extracting( LookupElement::getLookupString )
100+
.containsExactlyInAnyOrder(
101+
"make",
102+
"seatCount",
103+
"manufacturingYear",
104+
"myDriver",
105+
"passengers",
106+
"price",
107+
"category",
108+
"available"
109+
);
110+
111+
assertThat( myItems )
112+
.extracting( LookupElementPresentation::renderElement )
113+
.usingRecursiveFieldByFieldElementComparator()
114+
.containsExactlyInAnyOrder(
115+
createVariable( "make", "String" ),
116+
createVariable( "seatCount", "int" ),
117+
createVariable( "manufacturingYear", "String" ),
118+
createVariable( "myDriver", "PersonDto" ),
119+
createVariable( "passengers", "List<PersonDto>" ),
120+
createVariable( "price", "Long" ),
121+
createVariable( "category", "String" ),
122+
createVariable( "available", "boolean" )
123+
);
124+
}
125+
96126
private void assertCarAutoComplete() {
97127
assertThat( myItems )
98128
.extracting( LookupElement::getLookupString )
@@ -154,6 +184,11 @@ public void testCarMapperReturnTargetCarDtoWithBuilder() {
154184
assertCarDtoWithBuilderAutoComplete();
155185
}
156186

187+
public void testCarMapperReturnTargetCarDtoPublic() {
188+
configureByTestName();
189+
assertCarDtoPublicAutoComplete();
190+
}
191+
157192
public void testPersonMapperReturnTargetFluentPersonDto() {
158193
configureByTestName();
159194
assertFluentPersonDtoAutoComplete();
@@ -169,6 +204,11 @@ public void testCarMapperUpdateTargetCarDtoWithBuilder() {
169204
assertCarDtoWithBuilderAutoComplete();
170205
}
171206

207+
public void testCarMapperUpdateTargetCarDtoPublic() {
208+
configureByTestName();
209+
assertCarDtoPublicAutoComplete();
210+
}
211+
172212
public void testCarMapperUpdateTargetFluentCarDto() {
173213
configureByTestName();
174214
assertCarDtoAutoComplete();
@@ -341,6 +381,20 @@ public void testCarMapperReferenceTargetPropertyInCarDto() {
341381
} );
342382
}
343383

384+
public void testCarMapperReferencePublicTargetProperty() {
385+
myFixture.configureByFile( "CarMapperReferencePublicTargetProperty.java" );
386+
PsiElement reference = myFixture.getElementAtCaret();
387+
388+
assertThat( reference )
389+
.isInstanceOfSatisfying( PsiField.class, field -> {
390+
assertThat( field.getName() ).isEqualTo( "seatCount" );
391+
assertThat( field.getPresentation() ).isNotNull();
392+
assertThat( field.getPresentation().getPresentableText() ).isEqualTo( "seatCount" );
393+
assertThat( field.getType() ).isNotNull();
394+
assertThat( field.getType().getPresentableText() ).isEqualTo( "int" );
395+
} );
396+
}
397+
344398
public void testCarMapperReferenceTargetPropertyInCarDtoWithBuilder() {
345399
myFixture.configureByFile( "CarMapperReferenceBuilderTargetProperty.java" );
346400
PsiElement reference = myFixture.getElementAtCaret();

src/test/java/org/mapstruct/intellij/MapstructMethodUsagesSearcherTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,12 @@ public void testRenameBuilderTargetReferenceMethod() {
135135
myFixture.checkResultByFile( "RenameBuilderTargetReferenceAfter.java" );
136136
}
137137

138+
public void testRenamePublicTargetReferenceMethod() {
139+
myFixture.configureByFiles( "RenamePublicTargetReference.java" );
140+
myFixture.renameElementAtCaret( "newName" );
141+
myFixture.checkResultByFile( "RenamePublicTargetReferenceAfter.java" );
142+
}
143+
138144
public void testRenameSourceParameterReference() {
139145
myFixture.configureByFiles( "RenameSourceParameterReference.java" );
140146
myFixture.renameElementAtCaret( "param" );

src/test/java/org/mapstruct/intellij/rename/RenameHandlerTest.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,12 @@ public void testRenameFluentTargetParameter() {
4141
myFixture.checkResultByFile( "RenameFluentTargetParameterAfter.java" );
4242
}
4343

44+
public void testRenamePublicTargetParameter() {
45+
myFixture.configureByFile( "RenamePublicTargetParameter.java" );
46+
myFixture.renameElementAtCaret( "newName" );
47+
myFixture.checkResultByFile( "RenamePublicTargetParameterAfter.java" );
48+
}
49+
4450
public void testRenameMethodTargetParameter() {
4551
myFixture.configureByFile( "RenameTargetParameterForMethod.java" );
4652
myFixture.renameElementAtCaret( "newTarget" );
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.complex;
7+
8+
import java.util.List;
9+
10+
import org.mapstruct.Mapper;
11+
import org.mapstruct.Mapping;
12+
import org.mapstruct.Mappings;
13+
import org.example.dto.CarDtoPublic;
14+
import org.example.dto.PersonDto;
15+
import org.example.dto.Car;
16+
import org.example.dto.Person;
17+
18+
@Mapper
19+
public interface CarMapper {
20+
21+
@Mappings({
22+
@Mapping(source = "numberOfSeats", target = "seatCount<caret>"),
23+
@Mapping(source = "manufacturingDate", target = "manufacturingYear")
24+
})
25+
CarDtoPublic carToCarDto(Car car);
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.complex;
7+
8+
import java.util.List;
9+
10+
import org.mapstruct.Mapper;
11+
import org.mapstruct.Mapping;
12+
import org.mapstruct.Mappings;
13+
import org.example.dto.CarDtoPublic;
14+
import org.example.dto.PersonDto;
15+
import org.example.dto.Car;
16+
import org.example.dto.Person;
17+
18+
@Mapper
19+
public interface CarMapper {
20+
21+
@Mappings({
22+
@Mapping(source = "numberOfSeats", target = "<caret>seatCount"),
23+
@Mapping(source = "manufacturingDate", target = "manufacturingYear")
24+
})
25+
CarDtoPublic carToCarDto(Car car);
26+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at http://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.complex;
7+
8+
import java.util.List;
9+
10+
import org.mapstruct.Mapper;
11+
import org.mapstruct.Mapping;
12+
import org.mapstruct.MappingTarget;
13+
import org.mapstruct.Mappings;
14+
import org.example.dto.CarDtoPublic;
15+
import org.example.dto.PersonDto;
16+
import org.example.dto.Car;
17+
import org.example.dto.Person;
18+
19+
@Mapper
20+
public interface CarMapper {
21+
22+
@Mappings({
23+
@Mapping(source = "numberOfSeats", target = "<caret>seatCount"),
24+
@Mapping(source = "manufacturingDate", target = "manufacturingYear")
25+
})
26+
void carToCarDto(Car car, @MappingTarget CarDtoPublic target);
27+
}

0 commit comments

Comments
 (0)