-
Notifications
You must be signed in to change notification settings - Fork 15
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 read-write API generation support for Java code generation #100
Changes from 3 commits
4d37a96
595096b
f335349
9f4171b
475bebc
98db615
4c4de58
ba4e440
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,5 @@ | |
A: "hello", | ||
B: 12, | ||
C: ["foo", "bar", "baz"], | ||
D: 10e2 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,24 +3,71 @@ | |
import org.junit.jupiter.api.Test; | ||
import static org.junit.jupiter.api.Assertions.*; | ||
import java.util.ArrayList; | ||
import com.amazon.ion.system.IonReaderBuilder; | ||
import com.amazon.ion.IonReader; | ||
import com.amazon.ion.system.IonTextWriterBuilder; | ||
import com.amazon.ion.IonWriter; | ||
import com.amazon.ion.IonSystem; | ||
import com.amazon.ion.system.IonSystemBuilder; | ||
import com.amazon.ion.IonLoader; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.FileInputStream; | ||
import java.io.File; | ||
|
||
class CodeGenTest { | ||
// TODO: Add roundtrip tests after read-write APIs are supported for Java code generation | ||
private static final IonSystem ionSystem = IonSystemBuilder.standard().build(); | ||
private static final IonLoader ionLoader = ionSystem.getLoader(); | ||
|
||
@Test void getterAndSetterTestForStructWithFields() { | ||
desaikd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
ArrayList<String> a = new ArrayList<String>(); | ||
a.add("foo"); | ||
a.add("bar"); | ||
a.add("baz"); | ||
StructWithFields s = new StructWithFields("hello", 12, new AnonymousType2(a)); | ||
StructWithFields s = new StructWithFields("hello", 12, new AnonymousType2(a), 10e2); | ||
assertEquals("hello", s.getA(), "s.getA() should return \"hello\""); | ||
assertEquals(12, s.getB(), "s.getB() should return `12`"); | ||
assertEquals(3, s.getC().getValue().size(), "s.getC().getValue() should return ArrayList fo size 3"); | ||
assertEquals(10e2, s.getD(), "s.getD() should return `10e2`"); | ||
} | ||
|
||
@Test void getterAndSetterTestForNestedStruct() { | ||
NestedStruct n = new NestedStruct("hello", 12, new AnonymousType1(false)); | ||
assertEquals("hello", n.getA(), "n.getA() should return \"hello\""); | ||
assertEquals(12, n.getB(), "n.getB() should return `12`"); | ||
assertEquals(false, n.getC().getD(), "n.getC().getD() should return `false`"); | ||
NestedStruct n = new NestedStruct("hello", 12, new AnonymousType1(false)); | ||
assertEquals("hello", n.getA(), "n.getA() should return \"hello\""); | ||
assertEquals(12, n.getB(), "n.getB() should return `12`"); | ||
assertEquals(false, n.getC().getD(), "n.getC().getD() should return `false`"); | ||
} | ||
|
||
@Test void roundtripTestForStructWithFields() throws IOException { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (🗺️ PR tour) Added roundtrip tests for generated read-write APIs
popematt marked this conversation as resolved.
Show resolved
Hide resolved
|
||
File f = new File("./../../input/struct_with_fields.ion"); | ||
InputStream inputStream = new FileInputStream(f); | ||
IonTextWriterBuilder b = IonTextWriterBuilder.standard(); | ||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||
IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); | ||
try (IonReader reader = readerBuilder.build(inputStream)) { | ||
reader.next(); | ||
StructWithFields s = StructWithFields.readFrom(reader); | ||
IonWriter writer = b.build(out); | ||
s.writeTo(writer); | ||
writer.close(); | ||
} | ||
assertEquals(ionLoader.load(f), ionLoader.load(out.toByteArray())); | ||
} | ||
|
||
@Test void roundtripTestForNestedStruct() throws IOException { | ||
File f = new File("./../../input/nested_struct.ion"); | ||
InputStream inputStream = new FileInputStream(f); | ||
IonTextWriterBuilder b = IonTextWriterBuilder.standard(); | ||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||
IonReaderBuilder readerBuilder = IonReaderBuilder.standard(); | ||
try (IonReader reader = readerBuilder.build(inputStream)) { | ||
reader.next(); | ||
NestedStruct n = NestedStruct.readFrom(reader); | ||
IonWriter writer = b.build(out); | ||
n.writeTo(writer); | ||
writer.close(); | ||
} | ||
assertEquals(ionLoader.load(f), ionLoader.load(out.toByteArray())); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,10 @@ | ||
package {{ namespace }}; | ||
import java.util.ArrayList; | ||
import com.amazon.ion.IonReader; | ||
import com.amazon.ion.IonException; | ||
import com.amazon.ion.IonWriter; | ||
import com.amazon.ion.IonType; | ||
import java.io.IOException; | ||
|
||
public final class {{ target_kind_name }} { | ||
{% for field in fields -%} | ||
|
@@ -16,4 +21,110 @@ public final class {{ target_kind_name }} { | |
return this.{{ field.name | camel }}; | ||
} | ||
{% endfor %} | ||
|
||
desaikd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
public static {{ target_kind_name }} readFrom(IonReader reader) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (🗺️ PR tour) Added |
||
{# Intializes all the fields of this class #} | ||
{% for field in fields -%} | ||
{{ field.value }} {{ field.name | camel }} = | ||
desaikd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{% if field.value == "boolean" %} | ||
false | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If you use However, if it's not an optional field, then we're better off using the primitive types to avoid the overhead of the boxed types. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am going to keep this as boolean/int/double for now. I will change it in separate PR to consider nulls. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reference: #101 |
||
{% elif field.value == "int" or field.value == "double" %} | ||
0 | ||
{% else %} | ||
null | ||
{% endif %}; | ||
{% endfor %} | ||
{% if abstract_data_type == "Value"%} | ||
{# Reads `Value` class with a signle field `value` #} | ||
desaikd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
value = {% if fields[0].value | is_built_in_type %} | ||
{% if fields[0].value == "bytes[]" %} | ||
reader.newBytes(); | ||
desaikd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{% else %} | ||
reader.{{ fields[0].value | camel }}Value(); | ||
{% endif %} | ||
{% else %} | ||
{{ fields[0].value }}.readFrom(reader); | ||
{% endif %} | ||
{% elif abstract_data_type is object and abstract_data_type is containing("Structure") %} | ||
{# Reads `Structure` class with multiple fields based on `field.name` #} | ||
reader.stepIn(); | ||
while (reader.hasNext()) { | ||
reader.next(); | ||
String fieldName = reader.getFieldName(); | ||
switch(fieldName) { | ||
{% for field in fields %} | ||
case "{{ field.name }}": | ||
{{ field.name | camel }} = {% if field.value | is_built_in_type %} | ||
{% if field.value == "bytes[]" %} | ||
reader.newBytes(); | ||
{% else %} | ||
reader.{{ field.value | camel }}Value(); | ||
{% endif %} | ||
{% else %} | ||
{{ field.value }}.readFrom(reader); | ||
{% endif %} | ||
break; | ||
{% endfor %} | ||
default: | ||
throw new IonException("Can not read field name:" + fieldName + " for {{ target_kind_name }} as it doesn't exist in the given schema type definition."); | ||
} | ||
} | ||
reader.stepOut(); | ||
{% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} | ||
{# Reads `Sequence` class with a single field `value` that is an `ArraList` #} | ||
reader.stepIn(); | ||
value = new {{ fields[0].value }}(); | ||
{# Iterate through the `ArraList` and read each element in it based on the data type provided in `abstract_data_type[Sequence]` #} | ||
desaikd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
while (reader.hasNext()) { | ||
reader.next(); | ||
{% if abstract_data_type["Sequence"] | is_built_in_type == false %} | ||
value.add({{ abstract_data_type["Sequence"] }}.readFrom(reader)); | ||
{% else %} | ||
{% if abstract_data_type["Sequence"] == "bytes[]" %} | ||
value.add(reader.newBytes()); | ||
{% else %} | ||
value.add(reader.{{ abstract_data_type["Sequence"] | camel }}Value()); | ||
{% endif %} | ||
{% endif %} | ||
} | ||
reader.stepOut(); | ||
{% endif %} | ||
return new {{ target_kind_name }}({% for field in fields | sort(attribute="name") -%}{{ field.name | camel }}{% if not loop.last %},{% endif %}{% endfor %}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We really need to have a builder class so that we can avoid things like sorting the field names and having to check to see if we're on the last loop iteration to see if we need a comma. |
||
} | ||
|
||
public void writeTo(IonWriter writer) throws IOException { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (🗺️ PR tour) Added |
||
{% if abstract_data_type == "Value" %} | ||
{# Writes `Value` class with a signle field `value` as an Ion value #} | ||
desaikd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{% for field in fields %} | ||
{% if field.value | is_built_in_type == false %} | ||
this.{{ field.name | camel }}.writeTo(writer)?; | ||
{% else %} | ||
writer.write{% if field.isl_type_name == "symbol" %}Symbol{% else %}{{ field.isl_type_name | upper_camel }}{% endif %}(this.value); | ||
desaikd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{% endif %} | ||
{% endfor %} | ||
{% elif abstract_data_type is object and abstract_data_type is containing("Structure") %} | ||
{# Writes `Structure` class with multiple fields based on `field.name` as an Ion struct #} | ||
writer.stepIn(IonType.STRUCT); | ||
desaikd marked this conversation as resolved.
Show resolved
Hide resolved
|
||
{% for field in fields %} | ||
writer.setFieldName("{{ field.name }}"); | ||
{% if field.value | is_built_in_type == false %} | ||
this.{{ field.name | camel }}.writeTo(writer); | ||
{% else %} | ||
writer.write{% if field.isl_type_name == "symbol" %}Symbol{% else %}{{ field.isl_type_name | upper_camel }}{% endif %}(this.{{ field.name | camel }}); | ||
{% endif %} | ||
{% endfor %} | ||
writer.stepOut(); | ||
{% elif abstract_data_type is object and abstract_data_type is containing("Sequence") %} | ||
{# Writes `Sequence` class with a single field `value` that is an `ArraList` as an Ion sequence #} | ||
writer.stepIn(IonType.LIST); | ||
for ({{ abstract_data_type["Sequence"] }} value: this.value) { | ||
{% if abstract_data_type["Sequence"] | is_built_in_type == false %} | ||
value.writeTo(writer); | ||
{% else %} | ||
writer.write{% if fields[0].isl_type_name == "symbol" %}Symbol{% else %}{{ abstract_data_type["Sequence"] | upper_camel }}{% endif %}(value); | ||
{% endif %} | ||
} | ||
writer.stepOut(); | ||
{% endif %} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -88,7 +88,7 @@ impl Language for JavaLanguage { | |
} | ||
|
||
fn is_built_in_type(name: &str) -> bool { | ||
matches!(name, "int" | "String" | "boolean" | "byte[]" | "float") | ||
matches!(name, "int" | "String" | "boolean" | "byte[]" | "double") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (🗺️ PR tour) ion-java uses |
||
} | ||
|
||
fn template_name(template: &Template) -> String { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(🗺️ PR tour) Added this field to test for double value in Java.