Skip to content

Commit

Permalink
Create add method for initialized collections (#4)
Browse files Browse the repository at this point in the history
* Remove unused var

* Adding builder classes

* Add GeneratorFactory

* Handle addToCollection
  • Loading branch information
junkfactory authored Aug 5, 2024
1 parent 0839006 commit 97b236f
Show file tree
Hide file tree
Showing 15 changed files with 417 additions and 151 deletions.
123 changes: 97 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,48 +2,98 @@

# An opinionated Java Inner Builder Generator

This is an opinionated but simple Java Inner Builder Generator IntelliJ plugin that generates a
builder for a given class. The builder is an inner class of the class it is building.
This is an opinionated but simple Java Inner Builder Generator IntelliJ plugin that generates an
inner builder for a given class.

Based from [InnerBuilder](https://github.com/analytically/innerbuilder) with stripped down features.

Generates a builder class for a given class with the following features:

1. Generates builder method for final fields that are not initialized and/or static fields
1. Generates builder method for final fields that are not initialized, static fields are excluded
2. Generates static `builder()` method inside the parent class
3. Uses field names as setters in the builder
4. Detects collection types and generates `addTo...` method for them if they are initialized

Optional features:

1. Generates `toBuilder()` method to convert the object to a builder
2. Generates `validate()` method to validate the fields before building the object

<!-- Plugin description end -->
### Example

Initial

```java
public class Person {
static String m = "ME";
private static final Logger logger = Logger.getLogger("test");
private final String name;
private final int age;
private String lastName;

private List<String> list;

private final String name = "1";
private Set<String> set;

private final List<Address> addresses = new ArrayList<>();

}
```

Generates

```java
public class Person {
static String m = "ME";
private static final Logger logger = Logger.getLogger("test");
private final String name;
private final int age;
private String lastName;

private List<String> list;

private Set<String> set;

private final List<Address> addresses;

private Person(Builder builder) {
name = builder.name;
age = builder.age;
lastName = builder.lastName;
list = builder.list;
set = builder.set;
addresses = builder.addresses;
}

public static Builder builder() {
return new Builder();
}

public Builder toBuilder() {
Builder builder = new Builder();
builder.name = this.name;
builder.age = this.age;
builder.lastName = this.lastName;
builder.list = this.list;
builder.set = this.set;
builder.addresses = this.addresses;
return builder;
}

public static final class Builder {
private String name;
private int age;
private String lastName;
private List<String> list;
private Set<String> set;
private List<Address> addresses = new ArrayList<>();

private Builder() {
}

public Builder name(String name) {
this.name = name;
return this;
}

public Builder age(int age) {
this.age = age;
return this;
Expand All @@ -54,6 +104,21 @@ public class Person {
return this;
}

public Builder list(List<String> list) {
this.list = list;
return this;
}

public Builder set(Set<String> set) {
this.set = set;
return this;
}

public Builder addToAddresses(Address e) {
this.addresses.add(e);
return this;
}

private void validate() {
}

Expand All @@ -68,60 +133,66 @@ public class Person {
Supports Java record classes

```java
record Address(String street, String city, String state, String country) {
record Address(Person person, List<String> streets, String city) {
static String m = "ME";

}
```

Generates

```java
record Address(Person person, List<String> streets, String city) {
static String m = "ME";

public static Builder builder() {
return new Builder();
}

//optional toBuilder method
public Builder toBuilder() {
Builder builder = new Builder();
builder.street = this.street;
builder.person = this.person;
builder.streets = this.streets;
builder.city = this.city;
builder.state = this.state;
builder.country = this.country;
return builder;
}

public static final class Builder {
private String street;
private Person person;
private List<String> streets;
private String city;
private String state;
private String country;

private Builder() {
}

public Builder street(String street) {
this.street = street;
public Builder person(Person person) {
this.person = person;
return this;
}

public Builder city(String city) {
this.city = city;
public Builder streets(List<String> streets) {
this.streets = streets;
return this;
}

public Builder state(String state) {
this.state = state;
public Builder city(String city) {
this.city = city;
return this;
}

public Builder country(String country) {
this.country = country;
return this;
private void validate() {
}

//follows the containing class visibility
Address build() {
return new Address(street, city, state, country);
validate();
return new Address(person, streets, city);
}
}
}
```

<!-- Plugin description end -->

## Installation

1. Download plugin zip file from [Releases](https://github.com/junkfactory/java-inner-builder/releases)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import org.jetbrains.annotations.NotNull;

public class InnerBuilderAction extends BaseCodeInsightAction {
private final JavaInnerBuilderHandler handler = new JavaInnerBuilderHandler();
private static final JavaInnerBuilderHandler handler = new JavaInnerBuilderHandler();

@NotNull
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package com.github.junkfactory.innerbuilder;

import com.github.junkfactory.innerbuilder.generators.GeneratorFactory;
import com.github.junkfactory.innerbuilder.generators.GeneratorParams;
import com.github.junkfactory.innerbuilder.generators.PsiParams;
import com.github.junkfactory.innerbuilder.generators.Utils;
import com.github.junkfactory.innerbuilder.ui.JavaInnerBuilderOption;
import com.intellij.ide.util.PropertiesComponent;
import com.intellij.lang.LanguageCodeInsightActionHandler;
Expand All @@ -17,10 +21,13 @@
import java.util.EnumSet;
import java.util.Set;

import static com.github.junkfactory.innerbuilder.FieldCollector.collectFields;
import static com.github.junkfactory.innerbuilder.generators.FieldCollector.collectFields;
import static com.github.junkfactory.innerbuilder.ui.JavaInnerBuilderOptionSelector.selectFieldsAndOptions;

public class JavaInnerBuilderHandler implements LanguageCodeInsightActionHandler {
class JavaInnerBuilderHandler implements LanguageCodeInsightActionHandler {

private static final GeneratorFactory generatorFactory = GeneratorFactory.create();

@Override
public boolean isValidFor(final Editor editor, final PsiFile file) {
if (!(file instanceof PsiJavaFile)) {
Expand Down Expand Up @@ -64,25 +71,28 @@ public void invoke(@NotNull final Project project, @NotNull final Editor editor,
}

var existingFields = collectFields(file, editor);
if (!existingFields.isEmpty()) {
var selectedFields = selectFieldsAndOptions(existingFields, project);
if (selectedFields.isEmpty()) {
return;
}
var psiParams = PsiParams.builder()
.file(file)
.selectedFields(selectedFields)
.factory(JavaPsiFacade.getElementFactory(project))
.build();
var generatorParams = GeneratorParams.builder()
.project(project)
.editor(editor)
.psi(psiParams)
.options(currentOptions())
.build();
var builderGenerator = new InnerBuilderGenerator(generatorParams);
ApplicationManager.getApplication().runWriteAction(builderGenerator);
if (existingFields.isEmpty()) {
return;
}

var selectedFields = selectFieldsAndOptions(existingFields, project);
if (selectedFields.isEmpty()) {
return;
}

var psiParams = PsiParams.builder()
.file(file)
.selectedFields(selectedFields)
.factory(JavaPsiFacade.getElementFactory(project))
.build();
var generatorParams = GeneratorParams.builder()
.project(project)
.editor(editor)
.psi(psiParams)
.options(currentOptions())
.build();
var builderGenerator = generatorFactory.createInnerBuilderGenerator(generatorParams);
ApplicationManager.getApplication().runWriteAction(builderGenerator);
}

private Set<JavaInnerBuilderOption> currentOptions() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.github.junkfactory.innerbuilder;
package com.github.junkfactory.innerbuilder.generators;

import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
Expand All @@ -10,15 +10,17 @@
abstract class AbstractGenerator implements Runnable {

@NonNls
protected static final String BUILDER_CLASS_NAME = "Builder";
static final String BUILDER_CLASS_NAME = "Builder";
@NonNls
protected static final String BUILDER_METHOD_NAME = "builder";
static final String BUILDER_METHOD_NAME = "builder";
@NonNls
protected static final String TO_BUILDER_NAME = "toBuilder";
static final String TO_BUILDER_NAME = "toBuilder";

protected GeneratorParams generatorParams;
protected final GeneratorFactory generatorFactory;
protected final GeneratorParams generatorParams;

protected AbstractGenerator(GeneratorParams generatorParams) {
protected AbstractGenerator(GeneratorFactory generatorFactory, GeneratorParams generatorParams) {
this.generatorFactory = generatorFactory;
this.generatorParams = generatorParams;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.github.junkfactory.innerbuilder.generators;

import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiModifier;
import com.intellij.psi.util.PsiUtil;

class BuilderClassGenerator extends AbstractGenerator {

private final BuilderClassParams builderClassParams;
private final Runnable fieldsGenerator;
private final Runnable methodsGenerator;

BuilderClassGenerator(GeneratorFactory generatorFactory,
GeneratorParams generatorParams,
BuilderClassParams builderClassParams) {
super(generatorFactory, generatorParams);
this.builderClassParams = builderClassParams;
this.fieldsGenerator = generatorFactory.createBuilderFieldsGenerator(generatorParams, builderClassParams);
this.methodsGenerator = generatorFactory.createBuilderMethodsGenerator(generatorParams, builderClassParams);
}

@Override
public void run() {
//builder constructor
var builderClass = builderClassParams.builderClass();
var builderConstructor = generateBuilderConstructor();
addMethod(builderClass, null, builderConstructor, false);

fieldsGenerator.run();
methodsGenerator.run();
}

private PsiMethod generateBuilderConstructor() {
var builderConstructor = generatorParams.psi().factory().createConstructor(BUILDER_CLASS_NAME);
PsiUtil.setModifierProperty(builderConstructor, PsiModifier.PRIVATE, true);
return builderConstructor;
}

}
Loading

0 comments on commit 97b236f

Please sign in to comment.