Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds support for builder API generation in Java #154

Merged
merged 4 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,43 +22,35 @@ class CodeGenTest {
private static final IonSystem ionSystem = IonSystemBuilder.standard().build();
private static final IonLoader ionLoader = ionSystem.getLoader();

@Test void getterAndSetterTestForStructWithFields() {
StructWithFields s = new StructWithFields();
@Test void builderTestForStructWithFields() {
StructWithFields.Builder sb = new StructWithFields.Builder();
ArrayList<String> c = new ArrayList<String>();
c.add("foo");
c.add("bar");
c.add("baz");

// set all the fields of `StructWithFields`
s.setA("hello");
s.setB(12);
s.setD(10e2);
StructWithFields s = sb.a("hello").b(12).c(c).d(10e2).build();

// getter tests for `StructWithFields`
assertEquals("hello", s.getA(), "s.getA() should return \"hello\"");
assertEquals(12, s.getB(), "s.getB() should return `12`");
assertEquals(3, s.getC().size(), "s.getC() should return ArrayList fo size 3");
assertEquals(10e2, s.getD(), "s.getD() should return `10e2`");

// setter tests for `StructWithFields`
s.setA("hi");
assertEquals("hi", s.getA(), "s.getA() should return \"hi\"");
s.setB(6);
assertEquals(6, s.getB(), "s.getB() should return `6`");
s.setD(11e3);
assertEquals(11e3 ,s.getD(), "s.getD() should return `11e3`");
}

@Test void getterAndSetterTestForNestedStruct() {
@Test void builderTestForNestedStruct() {
// getter tests for `NestedStruct`
NestedStruct n = new NestedStruct();
NestedStruct.Builder nb = new NestedStruct.Builder();
ArrayList<Integer> e = new ArrayList<Integer>();
e.add(1);
e.add(2);
e.add(3);

// set all the fields of `NestedStruct`
n.setA("hello");
n.setB(12);
NestedStruct.NestedType1 n1 = new NestedStruct.NestedType1();
n1.setD(false);
n1.setE(e);
n.setC(n1);
NestedStruct.NestedType1.Builder nb1 = new NestedStruct.NestedType1.Builder();
NestedStruct.NestedType1 c = nb1.d(false).e(e).build();
NestedStruct n = nb.a("hello").b(12).c(c).build();

// getter tests for `NestedStruct`
assertEquals("hello", n.getA(), "n.getA() should return \"hello\"");
Expand Down Expand Up @@ -89,10 +81,6 @@ class CodeGenTest {

// getter tests for `Sequence`
assertEquals(3, s.getValue().size(), "s.getValue().size() should return ArrayList fo size 3");

// setter tests for `Sequence`
s.setValue(new ArrayList<String>());
assertEquals(true, s.getValue().isEmpty(), "s.getValue().isEmpty() should return `true`");
}

@Test void getterAndSetterTestForScalar() {
Expand Down
54 changes: 33 additions & 21 deletions src/bin/ion/commands/generate/templates/java/class.templ
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import java.io.IOException;
private {{ field_value.0 | fully_qualified_type_name }} {{ field_name | camel }};
{% endfor %}

public {{ model.name }}() {}
private {{ model.name }}() {}

{% for field_name, field_value in struct_info["fields"] -%}public {{ field_value.0 | fully_qualified_type_name }} get{% filter upper_camel %}{{ field_name }}{% endfilter %}() {
return this.{{ field_name | camel }};
Expand All @@ -39,6 +39,34 @@ import java.io.IOException;
}
{% endfor %}

public static class Builder {
{% for field_name, field_val in struct_info["fields"] -%}
{% set propertyName = field_name | camel %}
{% set PropertyType = field_val.0 | fully_qualified_type_name | wrapper_class %}

private {{ PropertyType }} {{ propertyName }};

public Builder {{ propertyName }}({{ PropertyType }} value) {
this.{{ propertyName }} = value;
return this;
}
{% endfor %}

public {{ model.name }} build() {
{{ model.name }} instance = new {{ model.name }}();
{% for field_name, field_val in struct_info["fields"] -%}
{% set propertyName = field_name | camel %}
{# field_val.1 is the field occurrence #}
{% if field_val.1 == "Required" %}
if ({{propertyName}} == null) {
throw new IllegalArgumentException("Missing required field {{propertyName}}");
}
{% endif %}
instance.{{ propertyName }} = {{ propertyName }};
{% endfor %}
return instance;
}
}

/**
* Reads a {{ model.name }} from an {@link IonReader}.
Expand All @@ -47,13 +75,8 @@ import java.io.IOException;
* The caller is responsible for positioning the reader on the value to read.
*/
public static {{ model.name }} readFrom(IonReader reader) {
{# Initializes all the fields of this class #}
{% for field_name, field_val in struct_info["fields"] -%}
{% set field_value = field_val.0 | fully_qualified_type_name %}
{% set field_occurrence = field_val.1 %}
{# Initialize all the required fields as `java.util.Optional` to verify if the required fields were missing or not #}
java.util.Optional<{{ field_value | wrapper_class }}> {{ field_name | camel }} = java.util.Optional.empty();
{% endfor %}
{# Initializes the builder for this class #}
Builder builder = new Builder();

{# Reads `Structure` class with multiple fields based on `field.name` #}
reader.stepIn();
Expand All @@ -66,7 +89,7 @@ import java.io.IOException;
{% set field_occurrence = field_val.1 %}
{% if field_occurrence == "Optional" %} {% set field_value = field_value | primitive_data_type %} {% endif %}
case "{{ field_name }}":
{{ field_name | camel }} = java.util.Optional.of(
builder.{{ field_name | camel }}(
{% if field_value | is_built_in_type %}
{% if field_value == "bytes[]" %}
reader.newBytes()
Expand All @@ -86,18 +109,7 @@ import java.io.IOException;
}
reader.stepOut();

{{ model.name }} {{ model.name | camel }} = new {{ model.name }}();
{% for field_name, field_val in struct_info["fields"] -%}
{# field_val.1 is the field occurrence #}
{% if field_val.1 == "Required" %}
if (!{{ field_name | camel }}.isPresent()) {
throw new IonException("Can not find field name: {{ field_name }} for {{ model.name }} while reading Ion data.");
}
{% endif %}
{{ model.name | camel }}.{{ field_name | camel }} = {{ field_name | camel }}.orElse(null);
{% endfor %}

return {{ model.name | camel }};
return builder.build();
}

/**
Expand Down
Loading