Skip to content

Commit

Permalink
Removes generating a separate class for nested sequence/scalar type
Browse files Browse the repository at this point in the history
* Adds changes to `Field` to store `AbstractDataType` information of the
field value
* Modified to `map_constraint_to_abstract_data_type` to consider nested
sequence/scalar type and change `AsbtractDataType` and `tera_fields`
accordingly
* Modified template to call `util_macros` for reading or writing a field
as sequence
  • Loading branch information
desaikd committed May 29, 2024
1 parent b2cfecd commit 1f17530
Show file tree
Hide file tree
Showing 5 changed files with 215 additions and 13 deletions.
141 changes: 136 additions & 5 deletions src/bin/ion/commands/beta/generate/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,50 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> {
code_gen_context,
)?;

// Verify that the current type doesn't contains any nested types and that they are also of sequence or scalar type.
// if found nested sequence/scalar types then remove them from nested types and set the sequence or scalar as a field in current class/struct.
if let Some(type_reference_name) = &type_name {
if type_reference_name.contains("NestedType") {
// This is a nested type. Check for the abstract data type. If it is sequence type or scalar type,
// then add them into the current tera fields and remove them from `nested_types`. Scalar and sequence types
// doesn't need to have a separate class/struct created for them.
if let Some(nested_type) = nested_types.get_mut(0) {
if matches!(
nested_type.abstract_data_type,
AbstractDataType::Sequence { .. }
) || nested_type.abstract_data_type == AbstractDataType::Value
{
// scalar and sequence types will only have 1 field. The field name here would be
// replaced with current `fields` constraint's field name.
// But `value_type` and ` isl_type_name` would be based on what we have in the `nested_type`.
let field = nested_type.fields.pop().unwrap();
self.generate_struct_field(
tera_fields,
L::target_type_as_sequence(&field.value_type),
field.isl_type_name,
"value",
Some(nested_type.abstract_data_type.to_owned()),
)?;

// change the `element_type` of current AbstractDataType::Sequence { .. }. This should be the type of nested type.
if let Some(AbstractDataType::Sequence { sequence_type, .. }) =
&code_gen_context.abstract_data_type
{
code_gen_context.abstract_data_type =
Some(AbstractDataType::Sequence {
element_type: field.value_type,
sequence_type: sequence_type.to_owned(),
});
}

// remove this nested type from the list as it will now be part of this field without generating separate nested type.
nested_types.pop();
return Ok(());
}
}
}
}

// if the abstract data type is a sequence then pass the type name as the updated `element_type`.
if let Some(AbstractDataType::Sequence {
element_type,
Expand All @@ -398,9 +442,10 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> {
L::target_type_as_sequence(element_type),
isl_type.name(),
"value",
None,
)?;
} else {
self.generate_struct_field(tera_fields, None, isl_type.name(), "value")?;
self.generate_struct_field(tera_fields, None, isl_type.name(), "value", None)?;
}
}
IslConstraintValue::Fields(fields, content_closed) => {
Expand All @@ -411,14 +456,43 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> {
code_gen_context,
)?;
for (name, value) in fields.iter() {
let type_name =
let mut type_name =
self.type_reference_name(value.type_reference(), nested_types)?;

let mut abstract_data_type = None;
let mut isl_type_name = value.type_reference().name();

if let Some(type_reference_name) = &type_name {
if type_reference_name.contains("NestedType") {
// This is a nested type. Check for the abstract data type. If it is sequence type or scalar type,
// then add them into the current tera fields and remove them from `nested_types`. Scalar and sequence types
// doesn't need to have a separate class/struct created for them.
if let Some(nested_type) = nested_types.get_mut(0) {
if matches!(
nested_type.abstract_data_type,
AbstractDataType::Sequence { .. }
) || nested_type.abstract_data_type == AbstractDataType::Value
{
// scalar and sequence types will only have 1 field. The field name here would be
// replaced with current `fields` constraint's field name.
// But `value_type` and ` isl_type_name` would be based on what we have in the `nested_type`.
let field = nested_type.fields.pop().unwrap();
abstract_data_type =
Some(nested_type.abstract_data_type.to_owned());
isl_type_name = field.isl_type_name;
type_name = field.value_type;

// remove this nested type from the list as it will now be part of this field without generating separate nested type.
nested_types.pop();
}
}
}
}
self.generate_struct_field(
tera_fields,
type_name,
value.type_reference().name(),
isl_type_name,
name,
abstract_data_type,
)?;
}
}
Expand All @@ -443,6 +517,52 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> {
code_gen_context,
)?;

// Verify that the current type doesn't contains any nested types and that they are of sequence or scalar type.
// if found nested sequence/scalar types then remove them from `nested_types` and set the sequence or scalar as a field in current class/struct.
if let Some(type_reference_name) = &type_name {
if type_reference_name.contains("NestedType") {
// This is a nested type. Check for the abstract data type. If it is sequence type or scalar type,
// then add them into the current tera fields and remove them from `nested_types`. Scalar and sequence types
// doesn't need to have a separate class/struct created for them.
if let Some(nested_type) = nested_types.get_mut(0) {
if matches!(
nested_type.abstract_data_type,
AbstractDataType::Sequence { .. }
) || nested_type.abstract_data_type == AbstractDataType::Value
{
// scalar and sequence types will only have 1 field. The field name here would be
// replaced with current `fields` constraint's field name.
// But `value_type` and ` isl_type_name` would be based on what we have in the `nested_type`.
let field = nested_type.fields.pop().unwrap();
self.generate_struct_field(
tera_fields,
field.value_type,
field.isl_type_name,
"value",
Some(nested_type.abstract_data_type.to_owned()),
)?;

// Update current `abstract_data_type` according to nested type
if let AbstractDataType::Sequence {
element_type: nested_element_type,
sequence_type: nested_sequence_type,
} = &nested_type.abstract_data_type
{
code_gen_context.abstract_data_type =
Some(AbstractDataType::Sequence {
element_type: nested_element_type.to_owned(),
sequence_type: nested_sequence_type.to_owned(),
});
}

// remove this nested type from the list as it will now be part of this field without generating separate nested type.
nested_types.pop();
return Ok(());
}
}
}
}

// if the abstract data type is a sequence then pass the type name as the updated `element_type`.
if let Some(AbstractDataType::Sequence { element_type, .. }) =
&code_gen_context.abstract_data_type
Expand All @@ -452,9 +572,16 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> {
L::target_type_as_sequence(element_type),
isl_type.name(),
"value",
None,
)?;
} else {
self.generate_struct_field(tera_fields, type_name, isl_type.name(), "value")?;
self.generate_struct_field(
tera_fields,
type_name,
isl_type.name(),
"value",
None,
)?;
}
}
_ => {}
Expand All @@ -469,11 +596,15 @@ impl<'a, L: Language + 'static> CodeGenerator<'a, L> {
abstract_data_type_name: Option<String>,
isl_type_name: String,
field_name: &str,
// This argument is used only for nested sequence type,
// it will be `None` in all other cases.
abstract_data_type: Option<AbstractDataType>,
) -> CodeGenResult<()> {
tera_fields.push(Field {
name: field_name.to_string(),
value_type: abstract_data_type_name,
isl_type_name,
abstract_data_type,
});
Ok(())
}
Expand Down
18 changes: 14 additions & 4 deletions src/bin/ion/commands/beta/generate/templates/java/class.templ
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
{# Includes the macros for anonymous types that will be added as child classes #}
{% import "nested_type.templ" as macros %}
{% import "util_macros.templ" as util_macros %}


package {{ namespace }};
import java.util.ArrayList;
Expand Down Expand Up @@ -83,7 +85,11 @@ public class {{ target_kind_name }} {
reader.{{ field.value_type | camel }}Value();
{% endif %}
{% else %}
{{ field.value_type }}.readFrom(reader);
{% if field.value_type is containing("ArrayList") %}
{{ util_macros::read_as_sequence(field=field) }}
{% else %}
{{ field.value_type }}.readFrom(reader);
{% endif %}
{% endif %}
break;
{% endfor %}
Expand All @@ -105,7 +111,7 @@ public class {{ target_kind_name }} {
{% endif %}
reader.stepIn();
value = new {{ fields[0].value_type }}();
{# Iterate through the `ArraList` and read each element in it based on the data type provided in `abstract_data_type[Sequence]` #}
{# Iterate through the `ArrayList` and read each element in it based on the data type provided in `abstract_data_type[Sequence]` #}
while (reader.hasNext()) {
reader.next();
{% if abstract_data_type["Sequence"].element_type | is_built_in_type == false %}
Expand Down Expand Up @@ -150,9 +156,13 @@ public class {{ target_kind_name }} {
{% for field in fields %}
writer.setFieldName("{{ field.name }}");
{% if field.value_type | is_built_in_type == false %}
this.{{ field.name | camel }}.writeTo(writer);
{% if field.value_type is containing("ArrayList") %}
{{ util_macros::write_as_sequence(field=field) }}
{% else %}
this.{{ field.name | camel }}.writeTo(writer);
{% endif %}
{% else %}
writer.write{{ field.isl_type_name | upper_camel }}(this.{{ field.name | camel }});
writer.write{{ field.isl_type_name | upper_camel }}(this.{{ field.name | camel }});
{% endif %}
{% endfor %}
writer.stepOut();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
{% import "util_macros.templ" as util_macros %}

{# following macro defines an anonymous type as children class for its parent type definition #}
{% macro nested_type(target_kind_name, fields, abstract_data_type, nested_anonymous_types) -%}
public static class {{ target_kind_name }} {
Expand Down Expand Up @@ -74,7 +76,11 @@
reader.{{ field.value_type | camel }}Value();
{% endif %}
{% else %}
{{ field.value_type }}.readFrom(reader);
{% if field.value_type is containing("ArrayList") %}
{{ util_macros::read_as_sequence(field=field) }}
{% else %}
{{ field.value_type }}.readFrom(reader);
{% endif %}
{% endif %}
break;
{% endfor %}
Expand All @@ -96,7 +102,7 @@
{% endif %}
reader.stepIn();
value = new {{ fields[0].value_type }}();
{# Iterate through the `ArraList` and read each element in it based on the data type provided in `abstract_data_type[Sequence]` #}
{# Iterate through the `ArrayList` and read each element in it based on the data type provided in `abstract_data_type[Sequence]` #}
while (reader.hasNext()) {
reader.next();
{% if abstract_data_type["Sequence"].element_type | is_built_in_type == false %}
Expand Down Expand Up @@ -142,9 +148,14 @@
{% for field in fields %}
writer.setFieldName("{{ field.name }}");
{% if field.value_type | is_built_in_type == false %}
this.{{ field.name | camel }}.writeTo(writer);

{% if field.value_type is containing("ArrayList") %}
{{ util_macros::write_as_sequence(field=field) }}
{% else %}
this.{{ field.name | camel }}.writeTo(writer);
{% endif %}
{% else %}
writer.write{{ field.isl_type_name | upper_camel }}(this.{{ field.name | camel }});
writer.write{{ field.isl_type_name | upper_camel }}(this.{{ field.name | camel }});
{% endif %}
{% endfor %}
writer.stepOut();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
{# following macro defines statements to read a class field as sequence #}
{% macro read_as_sequence(field) %}
new {{ field.value_type }}();
{# Reads `Sequence` field that is an `ArrayList` #}
{% if field.abstract_data_type["Sequence"].sequence_type == "List" %}
if(reader.getType() != IonType.LIST) {
throw new IonException("Expected list, found " + reader.getType() + " while reading {{ field.name | camel }}.");
}
{% elif field.abstract_data_type["Sequence"].sequence_type == "SExp" %}
if(reader.getType() != IonType.SEXP) {
throw new IonException("Expected sexpression, found " + reader.getType() + " while reading {{ field.name | camel }}.");
}
{% endif %}
reader.stepIn();
{# Iterate through the `ArrayList` and read each element in it based on the data type provided in `field.abstract_data_type[Sequence]` #}
while (reader.hasNext()) {
reader.next();
{% if field.abstract_data_type["Sequence"].element_type | is_built_in_type == false %}
{{ field.name | camel }}.add({{ field.abstract_data_type["Sequence"].element_type }}.readFrom(reader));
{% else %}
{% if field.abstract_data_type["Sequence"].element_type == "bytes[]" %}
{{ field.name | camel }}.add(reader.newBytes());
{% else %}
{{ field.name | camel }}.add(reader.{{ field.abstract_data_type["Sequence"].element_type | camel }}Value());
{% endif %}
{% endif %}
}
reader.stepOut();
{% endmacro %}
{# following macro defines statements to write a class field as sequence #}
{% macro write_as_sequence(field) %}
{# Writes `Sequence` field that is an `ArrayList` as an Ion sequence #}
{% if field.abstract_data_type["Sequence"].sequence_type == "List" %}
writer.stepIn(IonType.LIST);
{% else %}
writer.stepIn(IonType.SEXP);
{% endif %}
for ({{ field.abstract_data_type["Sequence"].element_type }} value: this.{{ field.name |camel }}) {
{% if field.abstract_data_type["Sequence"].element_type | is_built_in_type == false %}
value.writeTo(writer);
{% else %}
writer.write{{ field.abstract_data_type["Sequence"].element_type | upper_camel }}(value);
{% endif %}
}
writer.stepOut();
{% endmacro %}
4 changes: 4 additions & 0 deletions src/bin/ion/commands/beta/generate/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,14 @@ pub struct Field {
// name: value,
// value_type: None,
// isl_type_name: "list"
// abstract_data_type: None
// }
// Code generation process results into an Error when `value_type` is set to `None`
pub(crate) value_type: Option<String>,
pub(crate) isl_type_name: String,
// `abstract_data_type` is only used for sequence type fields. This value provides `element_type`
// and `sequence_type` information for this sequence type field.
pub(crate) abstract_data_type: Option<AbstractDataType>,
}

/// Represents an nested type that can be a part of another type definition.
Expand Down

0 comments on commit 1f17530

Please sign in to comment.