Skip to content

Commit 836d9bf

Browse files
authored
Handle refactors (#5)
* Handle refactors Separate generator interfaces * Fix addTo generation and cleanup deleted fields * Reuse addElement * Remove ancestor check
1 parent 97b236f commit 836d9bf

11 files changed

+130
-79
lines changed

src/main/java/com/github/junkfactory/innerbuilder/generators/AbstractGenerator.java

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,28 +24,34 @@ protected AbstractGenerator(GeneratorFactory generatorFactory, GeneratorParams g
2424
this.generatorParams = generatorParams;
2525
}
2626

27+
protected PsiElement addElement(PsiElement target, PsiElement element, PsiElement after) {
28+
if (after != null) {
29+
return target.addAfter(element, after);
30+
}
31+
return target.add(element);
32+
}
33+
2734
protected PsiElement addMethod(@NotNull final PsiClass target, @Nullable final PsiElement after,
2835
@NotNull final PsiMethod newMethod, final boolean replace) {
2936
var existingMethod = target.findMethodBySignature(newMethod, false);
3037
if (existingMethod == null && newMethod.isConstructor()) {
31-
for (final PsiMethod constructor : target.getConstructors()) {
32-
if (Utils.areParameterListsEqual(constructor.getParameterList(),
33-
newMethod.getParameterList())) {
34-
existingMethod = constructor;
35-
break;
36-
}
37-
}
38+
existingMethod = findConstructor(target, newMethod);
3839
}
3940
if (existingMethod == null) {
40-
if (after != null) {
41-
return target.addAfter(newMethod, after);
42-
} else {
43-
return target.add(newMethod);
44-
}
41+
return addElement(target, newMethod, after);
4542
} else if (replace) {
4643
existingMethod.replace(newMethod);
4744
}
4845
return existingMethod;
4946
}
5047

48+
private PsiMethod findConstructor(PsiClass target, PsiMethod newMethod) {
49+
for (var constructor : target.getConstructors()) {
50+
if (Utils.areParameterListsEqual(constructor.getParameterList(), newMethod.getParameterList())) {
51+
return constructor;
52+
}
53+
}
54+
return null;
55+
}
56+
5157
}

src/main/java/com/github/junkfactory/innerbuilder/generators/BuilderClassGenerator.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,12 @@
77
class BuilderClassGenerator extends AbstractGenerator {
88

99
private final BuilderClassParams builderClassParams;
10-
private final Runnable fieldsGenerator;
11-
private final Runnable methodsGenerator;
1210

1311
BuilderClassGenerator(GeneratorFactory generatorFactory,
1412
GeneratorParams generatorParams,
1513
BuilderClassParams builderClassParams) {
1614
super(generatorFactory, generatorParams);
1715
this.builderClassParams = builderClassParams;
18-
this.fieldsGenerator = generatorFactory.createBuilderFieldsGenerator(generatorParams, builderClassParams);
19-
this.methodsGenerator = generatorFactory.createBuilderMethodsGenerator(generatorParams, builderClassParams);
2016
}
2117

2218
@Override
@@ -26,7 +22,11 @@ public void run() {
2622
var builderConstructor = generateBuilderConstructor();
2723
addMethod(builderClass, null, builderConstructor, false);
2824

25+
var fieldsGenerator = generatorFactory.createBuilderFieldsGenerator(generatorParams, builderClassParams);
2926
fieldsGenerator.run();
27+
28+
var methodsGenerator = generatorFactory.createBuilderMethodsGenerator(generatorParams,
29+
builderClassParams, fieldsGenerator);
3030
methodsGenerator.run();
3131
}
3232

src/main/java/com/github/junkfactory/innerbuilder/generators/BuilderFieldsGenerator.java

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,17 @@
33
import com.intellij.codeInsight.generation.PsiFieldMember;
44
import com.intellij.psi.PsiClass;
55
import com.intellij.psi.PsiElement;
6+
import com.intellij.psi.PsiField;
67
import org.jetbrains.annotations.Nullable;
78

8-
class BuilderFieldsGenerator extends AbstractGenerator {
9+
import java.util.LinkedList;
10+
import java.util.List;
11+
import java.util.Optional;
12+
13+
class BuilderFieldsGenerator extends AbstractGenerator implements FieldsGenerator {
914

1015
private final BuilderClassParams builderClassParams;
16+
private final List<PsiField> fields = new LinkedList<>();
1117

1218
BuilderFieldsGenerator(GeneratorFactory generatorFactory,
1319
GeneratorParams generatorParams,
@@ -16,37 +22,56 @@ class BuilderFieldsGenerator extends AbstractGenerator {
1622
this.builderClassParams = builderClassParams;
1723
}
1824

25+
@Override
26+
public List<PsiField> getFields() {
27+
return fields;
28+
}
29+
1930
@Override
2031
public void run() {
21-
PsiElement lastAddedField = null;
32+
PsiField lastAddedField = null;
2233
for (var fieldMember : generatorParams.psi().selectedFields()) {
23-
lastAddedField = findOrCreateField(builderClassParams.builderClass(), fieldMember, lastAddedField);
34+
lastAddedField = createOrUpdateField(builderClassParams.builderClass(), fieldMember, lastAddedField);
35+
fields.add(lastAddedField);
2436
}
37+
cleanupFields(builderClassParams.builderClass());
2538
}
2639

27-
private PsiElement findOrCreateField(final PsiClass builderClass, final PsiFieldMember member,
40+
private void cleanupFields(PsiClass builderClass) {
41+
for (var field : builderClass.getFields()) {
42+
if (!fields.contains(field)) {
43+
deleteFieldAndMethodIfExists(builderClass, field);
44+
}
45+
}
46+
}
47+
48+
private PsiField createOrUpdateField(final PsiClass builderClass, final PsiFieldMember member,
2849
@Nullable final PsiElement last) {
2950
var psiFactory = generatorParams.psi().factory();
3051
var field = member.getElement();
3152
var fieldName = field.getName();
3253
var fieldType = field.getType();
3354
var existingField = builderClass.findFieldByName(fieldName, false);
3455
if (existingField == null || Utils.areTypesPresentableNotEqual(existingField.getType(), fieldType)) {
35-
if (existingField != null) {
36-
existingField.delete();
37-
}
56+
deleteFieldAndMethodIfExists(builderClass, existingField);
3857
var newField = psiFactory.createField(fieldName, fieldType);
3958
newField.setInitializer(field.getInitializer());
4059
if (!builderClassParams.targetClass().isRecord()) {
4160
field.setInitializer(null);
4261
}
43-
if (last != null) {
44-
return builderClass.addAfter(newField, last);
45-
} else {
46-
return builderClass.add(newField);
47-
}
62+
existingField = (PsiField) addElement(builderClass, newField, last);
4863
}
4964
return existingField;
5065
}
5166

67+
private void deleteFieldAndMethodIfExists(PsiClass builderClass, PsiField field) {
68+
if (null == field) {
69+
return;
70+
}
71+
Optional.ofNullable(field.getCopyableUserData(UserDataKey.METHOD_REF))
72+
.map(m -> builderClass.findMethodBySignature(m, false))
73+
.ifPresent(PsiElement::delete);
74+
field.delete();
75+
}
76+
5277
}

src/main/java/com/github/junkfactory/innerbuilder/generators/BuilderMethodsGenerator.java

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,41 @@
11
package com.github.junkfactory.innerbuilder.generators;
22

33
import com.github.junkfactory.innerbuilder.ui.JavaInnerBuilderOption;
4-
import com.intellij.codeInsight.generation.PsiFieldMember;
54
import com.intellij.openapi.util.text.StringUtil;
5+
import com.intellij.psi.PsiClass;
66
import com.intellij.psi.PsiElement;
7+
import com.intellij.psi.PsiField;
78
import com.intellij.psi.PsiMethod;
89
import com.intellij.psi.PsiModifier;
910
import com.intellij.psi.PsiStatement;
1011
import com.intellij.psi.util.PsiUtil;
1112

1213
import java.util.Objects;
14+
import java.util.Optional;
1315
import java.util.stream.Collectors;
1416
import java.util.stream.Stream;
1517

16-
class BuilderMethodsGenerator extends AbstractGenerator {
18+
class BuilderMethodsGenerator extends AbstractGenerator implements MethodsGenerator {
1719

1820
private final BuilderClassParams builderClassParams;
21+
private final FieldsGenerator fieldsGenerator;
1922

2023
BuilderMethodsGenerator(GeneratorFactory generatorFactory,
2124
GeneratorParams generatorParams,
22-
BuilderClassParams builderClassParams) {
25+
BuilderClassParams builderClassParams,
26+
FieldsGenerator fieldsGenerator) {
2327
super(generatorFactory, generatorParams);
2428
this.builderClassParams = builderClassParams;
29+
this.fieldsGenerator = fieldsGenerator;
2530
}
2631

2732
@Override
2833
public void run() {
2934
var builderClass = builderClassParams.builderClass();
3035
PsiElement lastAddedElement = null;
31-
for (var member : generatorParams.psi().selectedFields()) {
32-
var setterMethod = generateFieldMethod(member);
36+
for (var field : fieldsGenerator.getFields()) {
37+
var setterMethod = generateFieldMethod(field);
38+
field.putCopyableUserData(UserDataKey.METHOD_REF, setterMethod);
3339
lastAddedElement = addMethod(builderClass, lastAddedElement, setterMethod, false);
3440
}
3541

@@ -40,7 +46,7 @@ public void run() {
4046
}
4147

4248
var buildMethod = generateBuildMethod();
43-
addMethod(builderClass, null, buildMethod, builderClassParams.builderClass().isRecord());
49+
addMethod(builderClass, null, buildMethod, builderClassParams.targetClass().isRecord());
4450
}
4551

4652
private PsiMethod generateValidateMethod() {
@@ -51,16 +57,28 @@ private PsiMethod generateValidateMethod() {
5157
return validateMethod;
5258
}
5359

54-
private PsiMethod generateFieldMethod(PsiFieldMember member) {
55-
var addMethod = Utils.findFieldAddMethod(builderClassParams.builderClass(), member);
60+
private PsiMethod generateFieldMethod(PsiField field) {
61+
var addMethod = field.hasInitializer() ? findAddMethod(field) : null;
5662
if (null != addMethod) {
57-
return generateAddToCollection(member, addMethod);
63+
return generateAddToCollection(field, addMethod);
5864
}
59-
return generateBuilderSetter(member);
65+
return generateBuilderSetter(field);
6066
}
6167

62-
private PsiMethod generateAddToCollection(PsiFieldMember member, PsiMethod fieldAddMethod) {
63-
var field = member.getElement();
68+
private PsiMethod findAddMethod(PsiField field) {
69+
var fieldClass = PsiUtil.resolveClassInClassTypeOnly(field.getType());
70+
var methods = Optional.ofNullable(fieldClass)
71+
.map(PsiClass::getAllMethods)
72+
.orElseGet(() -> new PsiMethod[0]);
73+
for (var method : methods) {
74+
if (method.getName().equals("add") && method.getParameterList().getParametersCount() == 1) {
75+
return method;
76+
}
77+
}
78+
return null;
79+
}
80+
81+
private PsiMethod generateAddToCollection(PsiField field, PsiMethod fieldAddMethod) {
6482
//resolve the generic type of the collection via the parameter type of the add method
6583
var param = Objects.requireNonNull(fieldAddMethod.getParameterList().getParameter(0));
6684
var paramType = PsiUtil.resolveGenericsClassInType(field.getType())
@@ -84,9 +102,7 @@ private PsiMethod generateAddToCollection(PsiFieldMember member, PsiMethod field
84102
return addMethod;
85103
}
86104

87-
private PsiMethod generateBuilderSetter(final PsiFieldMember member) {
88-
89-
var field = member.getElement();
105+
private PsiMethod generateBuilderSetter(PsiField field) {
90106
var fieldType = field.getType();
91107
var fieldName = Utils.hasOneLetterPrefix(field.getName()) ?
92108
Character.toLowerCase(field.getName().charAt(1)) + field.getName().substring(2) : field.getName();

src/main/java/com/github/junkfactory/innerbuilder/generators/FieldCollector.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
import com.intellij.openapi.util.text.StringUtil;
66
import com.intellij.psi.JavaPsiFacade;
77
import com.intellij.psi.PsiClass;
8-
import com.intellij.psi.PsiElement;
98
import com.intellij.psi.PsiField;
109
import com.intellij.psi.PsiFile;
1110
import com.intellij.psi.PsiModifier;
@@ -45,17 +44,16 @@ public static List<PsiFieldMember> collectFields(final PsiFile file, final Edito
4544

4645
PsiClass classToExtractFieldsFrom = clazz;
4746
while (classToExtractFieldsFrom != null) {
48-
var classFieldMembers = collectFieldsInClass(element, clazz, classToExtractFieldsFrom);
47+
var classFieldMembers = collectFieldsInClass(clazz, classToExtractFieldsFrom);
4948
allFields.addAll(0, classFieldMembers);
5049
classToExtractFieldsFrom = classToExtractFieldsFrom.getSuperClass();
5150
}
5251

5352
return allFields;
5453
}
5554

56-
private static List<PsiFieldMember> collectFieldsInClass(final PsiElement element,
57-
final PsiClass accessObjectClass,
58-
final PsiClass classToExtractFieldsFrom) {
55+
private static List<PsiFieldMember> collectFieldsInClass(PsiClass accessObjectClass,
56+
PsiClass classToExtractFieldsFrom) {
5957
if (AbstractGenerator.BUILDER_CLASS_NAME.equals(classToExtractFieldsFrom.getName()) ||
6058
OBJECT_CLASS_NAME.equals(classToExtractFieldsFrom.getName())) {
6159
return List.of();
@@ -64,7 +62,6 @@ private static List<PsiFieldMember> collectFieldsInClass(final PsiElement elemen
6462
return Arrays.stream(classToExtractFieldsFrom.getFields())
6563
.filter(field -> helper.isAccessible(field, classToExtractFieldsFrom, accessObjectClass) ||
6664
hasSetter(classToExtractFieldsFrom, field.getName()))
67-
.filter(field -> !PsiTreeUtil.isAncestor(field, element, false))
6865
.filter(field -> !field.hasModifierProperty(PsiModifier.STATIC))
6966
.filter(field -> hasLowerCaseChar(field.getName()))
7067
.filter(field -> Objects.nonNull(field.getContainingClass()))
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.github.junkfactory.innerbuilder.generators;
2+
3+
import com.intellij.psi.PsiField;
4+
5+
import java.util.List;
6+
7+
public interface FieldsGenerator extends Runnable {
8+
List<PsiField> getFields();
9+
}

src/main/java/com/github/junkfactory/innerbuilder/generators/GeneratorFactory.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,10 @@ static GeneratorFactory create() {
1111
Runnable createBuilderClassGenerator(GeneratorParams generatorParams,
1212
BuilderClassParams builderClassParams);
1313

14-
Runnable createBuilderFieldsGenerator(GeneratorParams generatorParams,
15-
BuilderClassParams builderClassParams);
14+
FieldsGenerator createBuilderFieldsGenerator(GeneratorParams generatorParams,
15+
BuilderClassParams builderClassParams);
1616

17-
Runnable createBuilderMethodsGenerator(GeneratorParams generatorParams,
18-
BuilderClassParams builderClassParams);
17+
MethodsGenerator createBuilderMethodsGenerator(GeneratorParams generatorParams,
18+
BuilderClassParams builderClassParams,
19+
FieldsGenerator fieldsGenerator);
1920
}

src/main/java/com/github/junkfactory/innerbuilder/generators/InnerBuilderGeneratorFactory.java

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,15 @@ public Runnable createBuilderClassGenerator(GeneratorParams generatorParams,
1313
}
1414

1515
@Override
16-
public Runnable createBuilderFieldsGenerator(GeneratorParams generatorParams,
17-
BuilderClassParams builderClassParams) {
16+
public FieldsGenerator createBuilderFieldsGenerator(GeneratorParams generatorParams,
17+
BuilderClassParams builderClassParams) {
1818
return new BuilderFieldsGenerator(this, generatorParams, builderClassParams);
1919
}
2020

2121
@Override
22-
public Runnable createBuilderMethodsGenerator(GeneratorParams generatorParams,
23-
BuilderClassParams builderClassParams) {
24-
return new BuilderMethodsGenerator(this, generatorParams, builderClassParams);
22+
public MethodsGenerator createBuilderMethodsGenerator(GeneratorParams generatorParams,
23+
BuilderClassParams builderClassParams,
24+
FieldsGenerator fieldsGenerator) {
25+
return new BuilderMethodsGenerator(this, generatorParams, builderClassParams, fieldsGenerator);
2526
}
2627
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
package com.github.junkfactory.innerbuilder.generators;
2+
3+
public interface MethodsGenerator extends Runnable {
4+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.github.junkfactory.innerbuilder.generators;
2+
3+
import com.intellij.openapi.util.Key;
4+
import com.intellij.psi.PsiMethod;
5+
6+
final class UserDataKey {
7+
8+
private UserDataKey() {
9+
}
10+
11+
static final Key<PsiMethod> METHOD_REF = Key.create("METHOD_REF");
12+
13+
}

0 commit comments

Comments
 (0)