Skip to content

Commit

Permalink
fields can be used directly in inner builder (mjedynak#30)
Browse files Browse the repository at this point in the history
inner builder can assign value to class field directly
  • Loading branch information
blindpirate authored and mjedynak committed Sep 12, 2016
1 parent 569e5ae commit 069a4c2
Show file tree
Hide file tree
Showing 12 changed files with 187 additions and 24 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
*.ipr
*.iws
.idea
.idea/*
.idea/*
.gradle/*
build/*
out/*
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ public void run() {
PsiDirectory targetDirectory = createBuilderDialog.getTargetDirectory();
String className = createBuilderDialog.getClassName();
String methodPrefix = createBuilderDialog.getMethodPrefix();
List<PsiElementClassMember> fieldsToDisplay = getFieldsToIncludeInBuilder(psiClassFromEditor);
boolean innerBuilder = createBuilderDialog.isInnerBuilder();
List<PsiElementClassMember> fieldsToDisplay = getFieldsToIncludeInBuilder(psiClassFromEditor, innerBuilder);
MemberChooser<PsiElementClassMember> memberChooserDialog = memberChooserDialogFactory.getMemberChooserDialog(fieldsToDisplay, project);
memberChooserDialog.show();
writeBuilderIfNecessary(targetDirectory, className, methodPrefix, memberChooserDialog, createBuilderDialog);
Expand All @@ -75,8 +76,8 @@ private CreateBuilderDialog showDialog() {
return dialog;
}

private List<PsiElementClassMember> getFieldsToIncludeInBuilder(PsiClass clazz) {
return psiFieldSelector.selectFieldsToIncludeInBuilder(clazz);
private List<PsiElementClassMember> getFieldsToIncludeInBuilder(PsiClass clazz, boolean innerBuilder) {
return psiFieldSelector.selectFieldsToIncludeInBuilder(clazz, innerBuilder);
}

public void setPsiClassFromEditor(PsiClass psiClassFromEditor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class CreateBuilderDialogFactory {

static final String BUILDER_SUFFIX = "Builder";
static final String METHOD_PREFIX = "with";

private static final String DIALOG_NAME = "CreateBuilder";
private PsiHelper psiHelper;
private ReferenceEditorComboWithBrowseButtonFactory referenceEditorComboWithBrowseButtonFactory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,18 @@ public PsiFieldsForBuilderFactory(PsiFieldVerifier psiFieldVerifier) {
public PsiFieldsForBuilder createPsiFieldsForBuilder(List<PsiElementClassMember> psiElementClassMembers, PsiClass psiClass) {
List<PsiField> psiFieldsForSetters = new ArrayList<PsiField>();
List<PsiField> psiFieldsForConstructor = new ArrayList<PsiField>();
List<PsiField> allSelectedPsiFields = new ArrayList<PsiField>();
for (PsiElementClassMember psiElementClassMember : psiElementClassMembers) {
PsiElement psiElement = psiElementClassMember.getPsiElement();
if (psiElement instanceof PsiField) {
allSelectedPsiFields.add((PsiField) psiElement);
if (psiFieldVerifier.isSetInSetterMethod((PsiField) psiElement, psiClass)) {
psiFieldsForSetters.add((PsiField) psiElement);
} else if (psiFieldVerifier.isSetInConstructor((PsiField) psiElement, psiClass)) {
psiFieldsForConstructor.add((PsiField) psiElement);
}
}
}
return new PsiFieldsForBuilder(psiFieldsForSetters, psiFieldsForConstructor);
return new PsiFieldsForBuilder(psiFieldsForSetters, psiFieldsForConstructor, allSelectedPsiFields);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,11 @@
import pl.mjedynak.idea.plugins.builder.settings.CodeStyleSettings;
import pl.mjedynak.idea.plugins.builder.writer.BuilderContext;

import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;

import static com.intellij.openapi.util.text.StringUtil.isVowel;

Expand All @@ -37,6 +40,7 @@ public class BuilderPsiClassBuilder {

private List<PsiField> psiFieldsForSetters = null;
private List<PsiField> psiFieldsForConstructor = null;
private List<PsiField> allSelectedPsiFields = null;

private PsiClass builderClass = null;
private PsiElementFactory elementFactory = null;
Expand Down Expand Up @@ -70,12 +74,17 @@ private void initializeFields(BuilderContext context) {
srcClassFieldName = StringUtils.uncapitalize(srcClassName);
psiFieldsForSetters = context.getPsiFieldsForBuilder().getFieldsForSetters();
psiFieldsForConstructor = context.getPsiFieldsForBuilder().getFieldsForConstructor();
allSelectedPsiFields = context.getPsiFieldsForBuilder().getAllSelectedFields();
methodCreator = new MethodCreator(elementFactory, builderClassName);
butMethodCreator = new ButMethodCreator(elementFactory);
}

public BuilderPsiClassBuilder withFields() {
psiFieldsModifier.modifyFields(psiFieldsForSetters, psiFieldsForConstructor, builderClass);
if (isInnerBuilder(builderClass)) {
psiFieldsModifier.modifyFieldsForInnerClass(allSelectedPsiFields, builderClass);
} else {
psiFieldsModifier.modifyFields(psiFieldsForSetters, psiFieldsForConstructor, builderClass);
}
return this;
}

Expand All @@ -95,15 +104,25 @@ public BuilderPsiClassBuilder withInitializingMethod() {
}

public BuilderPsiClassBuilder withSetMethods(String methodPrefix) {
for (PsiField psiFieldForSetter : psiFieldsForSetters) {
createAndAddMethod(psiFieldForSetter, methodPrefix);
}
for (PsiField psiFieldForConstructor : psiFieldsForConstructor) {
createAndAddMethod(psiFieldForConstructor, methodPrefix);
if (isInnerBuilder(builderClass)) {
for (PsiField psiFieldForAssignment : allSelectedPsiFields) {
createAndAddMethod(psiFieldForAssignment, methodPrefix);
}
} else {
for (PsiField psiFieldForSetter : psiFieldsForSetters) {
createAndAddMethod(psiFieldForSetter, methodPrefix);
}
for (PsiField psiFieldForConstructor : psiFieldsForConstructor) {
createAndAddMethod(psiFieldForConstructor, methodPrefix);
}
}
return this;
}

private boolean isInnerBuilder(PsiClass aClass) {
return aClass.hasModifierProperty("static");
}

public BuilderPsiClassBuilder withButMethod() {
PsiMethod method = butMethodCreator.butMethod(builderClassName, builderClass, srcClass);
builderClass.add(method);
Expand All @@ -117,7 +136,7 @@ private void createAndAddMethod(PsiField psiField, String methodPrefix) {
public PsiClass build() {
StringBuilder buildMethodText = new StringBuilder();
appendConstructor(buildMethodText);
appendSetMethods(buildMethodText);
appendSetMethodsOrAssignments(buildMethodText);
buildMethodText.append("return ").append(srcClassFieldName).append(";}");
PsiMethod buildMethod = elementFactory.createMethodFromText(buildMethodText.toString(), srcClass);
builderClass.add(buildMethod);
Expand All @@ -130,8 +149,18 @@ private void appendConstructor(StringBuilder buildMethodText) {
.append(srcClassFieldName).append(" = new ").append(srcClassName).append("(").append(constructorParameters).append(");");
}

private void appendSetMethods(StringBuilder buildMethodText) {
for (PsiField psiFieldsForSetter : psiFieldsForSetters) {
private void appendSetMethodsOrAssignments(StringBuilder buildMethodText) {
appendSetMethods(buildMethodText, psiFieldsForSetters);
if (isInnerBuilder(builderClass)) {
Set<PsiField> fieldsSetViaAssignment = new HashSet<PsiField>(allSelectedPsiFields);
fieldsSetViaAssignment.removeAll(psiFieldsForSetters);
fieldsSetViaAssignment.removeAll(psiFieldsForConstructor);
appendAssignments(buildMethodText, fieldsSetViaAssignment);
}
}

private void appendSetMethods(StringBuilder buildMethodText, Collection<PsiField> fieldsBeSetViaSetter) {
for (PsiField psiFieldsForSetter : fieldsBeSetViaSetter) {
String fieldNamePrefix = codeStyleSettings.getFieldNamePrefix();
String fieldName = psiFieldsForSetter.getName();
String fieldNameWithoutPrefix = fieldName.replaceFirst(fieldNamePrefix, "");
Expand All @@ -140,6 +169,14 @@ private void appendSetMethods(StringBuilder buildMethodText) {
}
}

private void appendAssignments(StringBuilder buildMethodText, Collection<PsiField> fieldsSetViaAssignment) {
for (PsiField field : fieldsSetViaAssignment) {
buildMethodText.append(srcClassFieldName).append(".")
.append(field.getName()).append("=").append("this.")
.append(field.getName()).append(";");
}
}

private String createConstructorParameters() {
StringBuilder sb = new StringBuilder();
for (PsiField psiField : psiFieldsForConstructor) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@ public PsiFieldSelector(PsiElementClassMemberFactory psiElementClassMemberFactor
this.psiFieldVerifier = psiFieldVerifier;
}

public List<PsiElementClassMember> selectFieldsToIncludeInBuilder(final PsiClass psiClass) {
public List<PsiElementClassMember> selectFieldsToIncludeInBuilder(final PsiClass psiClass, final boolean innerBuilder) {
List<PsiElementClassMember> result = new ArrayList<PsiElementClassMember>();

List<PsiField> psiFields = Arrays.asList(psiClass.getAllFields());
Iterable<PsiField> filtered = filter(psiFields, new Predicate<PsiField>() {
@Override
public boolean apply(PsiField psiField) {
return isAppropriate(psiClass, psiField);
return innerBuilder || isAppropriate(psiClass, psiField);
}
});

Expand All @@ -41,7 +42,6 @@ public boolean apply(PsiField psiField) {

private boolean isAppropriate(PsiClass psiClass, PsiField psiField) {
return psiFieldVerifier.isSetInSetterMethod(psiField, psiClass) || psiFieldVerifier.isSetInConstructor(psiField, psiClass);

}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ public void modifyFields(List<PsiField> psiFieldsForSetters, List<PsiField> psiF
}
}

public void modifyFieldsForInnerClass(List<PsiField> allFileds, PsiClass innerBuilderClass) {
for (PsiField field : allFileds) {
removeModifiers(field, innerBuilderClass);
}
}

private void removeModifiers(PsiField psiField, PsiClass builderClass) {
PsiElement copy = psiField.copy();
removeAnnotationsFromElement(copy);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@ public class PsiFieldsForBuilder {

private List<PsiField> psiFieldsForSetters;
private List<PsiField> psiFieldsForConstructor;
private List<PsiField> allSelectedPsiFields;

public PsiFieldsForBuilder(List<PsiField> psiFieldsForSetters, List<PsiField> psiFieldsForConstructor) {
public PsiFieldsForBuilder(List<PsiField> psiFieldsForSetters, List<PsiField> psiFieldsForConstructor, List<PsiField> allSelectedPsiFields) {
this.psiFieldsForSetters = ImmutableList.copyOf(psiFieldsForSetters);
this.psiFieldsForConstructor = ImmutableList.copyOf(psiFieldsForConstructor);
this.allSelectedPsiFields = ImmutableList.copyOf(allSelectedPsiFields);
}

public List<PsiField> getFieldsForSetters() {
Expand All @@ -22,4 +24,8 @@ public List<PsiField> getFieldsForSetters() {
public List<PsiField> getFieldsForConstructor() {
return psiFieldsForConstructor;
}

public List<PsiField> getAllSelectedFields() {
return allSelectedPsiFields;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ public void shouldDisplayCreateBuilderAndMemberChooserDialogAndWriteBuilderWhenO
given(createBuilderDialog.getMethodPrefix()).willReturn(methodPrefix);
given(psiClassFromEditor.getAllFields()).willReturn(allFields);
given(memberChooserDialogFactory.getMemberChooserDialog(selectedFields, project)).willReturn(memberChooserDialog);
given(psiFieldSelector.selectFieldsToIncludeInBuilder(psiClassFromEditor)).willReturn(selectedFields);
given(psiFieldSelector.selectFieldsToIncludeInBuilder(psiClassFromEditor, false)).willReturn(selectedFields);
given(memberChooserDialog.getSelectedElements()).willReturn(selectedFields);

// when
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public class BuilderPsiClassBuilderTest {
private BuilderContext context;
private List<PsiField> psiFieldsForSetters = new ArrayList<PsiField>();
private List<PsiField> psiFieldsForConstructor = new ArrayList<PsiField>();
private List<PsiField> allSelectedPsiFields = new ArrayList<PsiField>();

private String builderClassName = "BuilderClassName";
private String srcClassName = "ClassName";
Expand All @@ -73,6 +74,7 @@ public void setUp() {
given(srcClass.getName()).willReturn(srcClassName);
given(psiFieldsForBuilder.getFieldsForConstructor()).willReturn(psiFieldsForConstructor);
given(psiFieldsForBuilder.getFieldsForSetters()).willReturn(psiFieldsForSetters);
given(psiFieldsForBuilder.getAllSelectedFields()).willReturn(allSelectedPsiFields);
given(elementFactory.createClass(builderClassName)).willReturn(builderClass);
given(builderClass.getModifierList()).willReturn(psiModifierList);
context = new BuilderContext(project, psiFieldsForBuilder, targetDirectory, builderClassName, srcClass, "anyPrefix", false, false);
Expand Down Expand Up @@ -186,6 +188,29 @@ public void shouldAddSetMethodsForFieldsFromBothLists() {
verify(builderClass).add(methodForFieldForConstructor);
}

@Test
public void shouldAddAllSelectedFieldAsSetterInInnerBuilder() {
// giver
PsiField selectedField = mock(PsiField.class);
allSelectedPsiFields.add(selectedField);

String methodPrefix = "with";

PsiMethod setterMethod = mock(PsiMethod.class);
given(methodCreator.createMethod(selectedField, methodPrefix)).willReturn(setterMethod);

BuilderPsiClassBuilder builder = psiClassBuilder.anInnerBuilder(context);
setField(builder, "methodCreator", methodCreator);

given(builderClass.hasModifierProperty("static")).willReturn(true);

// when
builder.withSetMethods(methodPrefix);

// then
verify(builderClass).add(setterMethod);
}

@Test
public void shouldAddButMethod() {
// given
Expand Down Expand Up @@ -223,6 +248,70 @@ public void shouldReturnBuilderObjectWithBuildMethodUsingSetterAndConstructor()
assertThat(result).isNotNull();
}

@Test
public void constructorShouldHavePriorityOverSetter() {
// given
PsiField nameField = mock(PsiField.class);
PsiField ageField = mock(PsiField.class);
given(nameField.getName()).willReturn("name");
given(ageField.getName()).willReturn("age");

psiFieldsForConstructor.clear();
psiFieldsForSetters.clear();
allSelectedPsiFields.clear();
psiFieldsForConstructor.add(nameField);
psiFieldsForSetters.add(ageField);

PsiMethod method = mock(PsiMethod.class);
String expectedCode = "public " + srcClassName + " build() { "
+ srcClassName + " " + srcClassFieldName + " = new " + srcClassName + "(name);"
+ srcClassFieldName + ".setAge(age);return " + srcClassFieldName + ";}";
given(elementFactory.createMethodFromText(expectedCode, srcClass)).willReturn(method);

given(builderClass.hasModifierProperty("static")).willReturn(true);

// when
PsiClass result = psiClassBuilder.anInnerBuilder(context).build();

// then
verify(builderClass).add(method);
assertThat(result).isNotNull();

}

@Test
public void setterShouldHavePriorityOverField() {
// given
PsiField nameField = mock(PsiField.class);
PsiField ageField = mock(PsiField.class);
given(nameField.getName()).willReturn("name");
given(ageField.getName()).willReturn("age");

psiFieldsForConstructor.clear();
psiFieldsForSetters.clear();
allSelectedPsiFields.clear();
psiFieldsForSetters.add(nameField);
allSelectedPsiFields.add(nameField);
allSelectedPsiFields.add(ageField);

PsiMethod method = mock(PsiMethod.class);
String expectedCode = "public " + srcClassName + " build() { "
+ srcClassName + " " + srcClassFieldName + " = new " + srcClassName + "();"
+ srcClassFieldName + ".setName(name);"
+ srcClassFieldName + ".age=this.age;return " + srcClassFieldName + ";}";
given(elementFactory.createMethodFromText(expectedCode, srcClass)).willReturn(method);

given(builderClass.hasModifierProperty("static")).willReturn(true);

// when
PsiClass result = psiClassBuilder.anInnerBuilder(context).build();

// then
verify(builderClass).add(method);
assertThat(result).isNotNull();

}

@SuppressWarnings("unchecked")
private void assertFieldsAreSet(BuilderPsiClassBuilder result) {
assertThat(result).isSameAs(psiClassBuilder);
Expand Down
Loading

0 comments on commit 069a4c2

Please sign in to comment.