Skip to content

Commit 19196bd

Browse files
committed
add generics support
1 parent 37cbb5a commit 19196bd

7 files changed

Lines changed: 176 additions & 10 deletions

File tree

‎src/main/java/org/mapstruct/intellij/codeinsight/references/MapstructBaseReference.java‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.intellij.psi.PsiReferenceExpression;
1919
import com.intellij.psi.PsiType;
2020
import com.intellij.psi.util.PsiTreeUtil;
21+
import com.intellij.psi.util.PsiUtil;
2122
import com.intellij.util.IncorrectOperationException;
2223
import org.jetbrains.annotations.NotNull;
2324
import org.jetbrains.annotations.Nullable;
@@ -148,6 +149,24 @@ public final Object[] getVariants() {
148149
@Nullable
149150
abstract PsiType resolvedType();
150151

152+
/**
153+
* Substitutes type parameters in {@code memberType} using the generic context from the previous reference in the
154+
* property chain.
155+
*/
156+
@Nullable
157+
protected PsiType substituteMemberType(@Nullable PsiType memberType) {
158+
if ( memberType == null || previous == null ) {
159+
return memberType;
160+
}
161+
162+
PsiType ownerType = previous.resolvedType();
163+
if ( ownerType == null ) {
164+
return memberType;
165+
}
166+
167+
return PsiUtil.resolveGenericsClassInType( ownerType ).getSubstitutor().substitute( memberType );
168+
}
169+
151170
@Override
152171
public PsiElement handleElementRename(@NotNull String newElementName) throws IncorrectOperationException {
153172
PsiElement reference = resolve();

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -135,16 +135,16 @@ PsiType resolvedType() {
135135
PsiElement element = resolve();
136136

137137
if ( element instanceof PsiMethod psiMethod ) {
138-
return psiMethod.getReturnType();
138+
return substituteMemberType( psiMethod.getReturnType() );
139139
}
140140
else if ( element instanceof PsiParameter psiParameter ) {
141141
return psiParameter.getType();
142142
}
143143
else if ( element instanceof PsiRecordComponent psiRecordComponent ) {
144-
return psiRecordComponent.getType();
144+
return substituteMemberType( psiRecordComponent.getType() );
145145
}
146146
else if ( element instanceof PsiField psiField ) {
147-
return psiField.getType();
147+
return substituteMemberType( psiField.getType() );
148148
}
149149

150150
return null;

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -205,16 +205,16 @@ PsiType resolvedType() {
205205
PsiElement element = resolve();
206206

207207
if ( element instanceof PsiMethod psiMethod ) {
208-
return firstParameterPsiType( psiMethod );
208+
return substituteMemberType( firstParameterPsiType( psiMethod ) );
209209
}
210210
else if ( element instanceof PsiParameter psiParameter ) {
211211
return psiParameter.getType();
212212
}
213213
else if ( element instanceof PsiRecordComponent psiRecordComponent ) {
214-
return psiRecordComponent.getType();
214+
return substituteMemberType( psiRecordComponent.getType() );
215215
}
216216
else if ( element instanceof PsiField psiField ) {
217-
return psiField.getType();
217+
return substituteMemberType( psiField.getType() );
218218
}
219219

220220
return null;

‎src/test/java/org/mapstruct/intellij/MapstructBaseCompletionTestCase.java‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,15 @@
55
*/
66
package org.mapstruct.intellij;
77

8-
import java.io.File;
9-
108
import com.intellij.codeInsight.completion.LightFixtureCompletionTestCase;
119
import com.intellij.openapi.util.text.StringUtil;
12-
import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess;
1310
import com.intellij.testFramework.LightProjectDescriptor;
1411
import com.intellij.testFramework.PsiTestUtil;
1512
import com.intellij.util.PathUtil;
1613
import org.jetbrains.annotations.NotNull;
1714

15+
import java.io.File;
16+
1817
/**
1918
* Base completion test case for MapStruct.
2019
*
@@ -29,7 +28,13 @@ protected void setUp() throws Exception {
2928
super.setUp();
3029
final String mapstructLibPath = PathUtil.toSystemIndependentName( new File( BUILD_LIBS_DIRECTORY )
3130
.getAbsolutePath() );
32-
VfsRootAccess.allowRootAccess( getTestRootDisposable(), mapstructLibPath );
31+
32+
allowAccessToDirsIfExists(
33+
BUILD_LIBS_DIRECTORY,
34+
"testData",
35+
"build/test-libs"
36+
);
37+
3338
PsiTestUtil.addLibrary(
3439
myFixture.getProjectDisposable(),
3540
myFixture.getModule(),

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

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,36 @@ public void testFluentGenericTargetMapper() {
488488
);
489489
}
490490

491+
public void testGenericCarWrapperSourceAutoCompleteAfterCar() {
492+
configureByTestName();
493+
494+
assertThat( myItems )
495+
.extracting( LookupElement::getLookupString )
496+
.containsExactlyInAnyOrder(
497+
"winCode"
498+
);
499+
500+
assertThat( myItems )
501+
.extracting( LookupElementPresentation::renderElement )
502+
.usingRecursiveFieldByFieldElementComparator()
503+
.containsExactlyInAnyOrder(
504+
createVariable( "winCode", "String" )
505+
);
506+
}
507+
508+
public void testGenericCarWrapperTargetAutoCompleteAfterCar() {
509+
configureByTestName();
510+
511+
assertThat( myItems )
512+
.extracting( LookupElement::getLookupString )
513+
.containsExactlyInAnyOrder( "winCode" );
514+
515+
assertThat( myItems )
516+
.extracting( LookupElementPresentation::renderElement )
517+
.usingRecursiveFieldByFieldElementComparator()
518+
.containsExactlyInAnyOrder( createVariable( "winCode", "String" ) );
519+
}
520+
491521
public void testVariantsCarMapperNoSourceClass() {
492522
myFixture.configureByFile( "CarMapperNoSourceClass.java" );
493523
complete();
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.complex;
7+
8+
import org.mapstruct.Mapper;
9+
import org.mapstruct.Mapping;
10+
11+
@Mapper
12+
public interface GenericCarWrapperMapper {
13+
14+
@Mapping(target = "id", source = "wrapper.car.<caret>winCode")
15+
CarEntity toCarDto(CarWrapper<Car> wrapper);
16+
}
17+
18+
class CarEntity {
19+
20+
private String id;
21+
22+
public String getId() {
23+
return id;
24+
}
25+
26+
public void setId(String id) {
27+
this.id = id;
28+
}
29+
30+
}
31+
32+
class Car {
33+
34+
private String winCode;
35+
36+
public String getWinCode() {
37+
return winCode;
38+
}
39+
40+
public void setWinCode(String winCode) {
41+
this.winCode = winCode;
42+
}
43+
}
44+
45+
class CarWrapper<T> {
46+
47+
private T car;
48+
49+
public T getCar() {
50+
return car;
51+
}
52+
53+
public void setCar(T car) {
54+
this.car = car;
55+
}
56+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright MapStruct Authors.
3+
*
4+
* Licensed under the Apache License version 2.0, available at https://www.apache.org/licenses/LICENSE-2.0
5+
*/
6+
package org.mapstruct.ap.test.complex;
7+
8+
import org.mapstruct.Mapper;
9+
import org.mapstruct.Mapping;
10+
import org.mapstruct.MappingTarget;
11+
12+
@Mapper
13+
public interface GenericCarWrapperTargetMapper {
14+
15+
@Mapping(source = "id", target = "wrapper.car.<caret>winCode")
16+
void update(@MappingTarget CarWrapper<Car> wrapper, CarEntity entity);
17+
}
18+
19+
class CarEntity {
20+
21+
private String id;
22+
23+
public String getId() {
24+
return id;
25+
}
26+
27+
public void setId(String id) {
28+
this.id = id;
29+
}
30+
}
31+
32+
class Car {
33+
34+
private String winCode;
35+
36+
public String getWinCode() {
37+
return winCode;
38+
}
39+
40+
public void setWinCode(String winCode) {
41+
this.winCode = winCode;
42+
}
43+
}
44+
45+
class CarWrapper<T> {
46+
47+
private T car;
48+
49+
public T getCar() {
50+
return car;
51+
}
52+
53+
public void setCar(T car) {
54+
this.car = car;
55+
}
56+
}

0 commit comments

Comments
 (0)