eclipse-aas4j / aas4j-model-generator

Apache License 2.0
2 stars 3 forks source link

Simplify ENUM usage #5

Open dglachs opened 2 years ago

dglachs commented 2 years ago

Hello all,

this is a proposal for improvement ...

currently, the generation of enums simply transforms the name of the class name to a SCREAMING_SNAKE_CASE notation. During de/serialization an extra Serializer/Deserializer has to take care of again transforming the SCREAMING_SNAKE_CASE into the serialization value (CamelCase), e.g.

ASSET_ADMINISTRATION_SHELL --> AssetAdministrationShell SUBMODEL --> Submodel SUBMODEL_ELEMENT --> SubmodelElement

this works well, when the transformation is always according to this transformation rule. However, this approach has several drawbacks:

  1. it does not work for the DataTypeDefXsd- Enumeration where the serialization value should be "xs:anyURI, xs:int etc."
  2. the de/serialization module wants to transform every enum, hence the configuration

module.addSerializer(Enum.class, new EnumSerializer()); cannot be used with a ObjectMapper for example with SpringBoot.

To simplify the use of enums with the AAS Model, i'd propose to keep the "serialized" value of the enumeraton such as

ASSET_ADMINISTRATION_SHELL("AssetAministrationShell") in KeyTypes or ANY_URI("xs:anyURI") in DataTypeDefXsd

This can be achieved by changing few templates:

and by adding a new template

all of the files are attached!

changes to the distinct files are as follows:

enum-default-methods.rq

Simply generate a constructor, override the toString() method and add a static "fromValue(String text)" method. Optionally (?useJackson) add the required JsonAnnotations for @JsonValue and @JsonCreator (adopt basic-imports.rq)!

    "\n"
    "\n\t" st:call-template(idstt:to_enum_name, ?class) "(String value) {"
    "\n\t\tthis.value = value;"
    "\n\t}"
    "\n"
    "\n"
    if(?useJackson, "\n\t@JsonValue", "")
    "\n\t@Override"
    "\n\tpublic String toString() {"
    "\n\t\treturn value.toString();"
    "\n\t}"
    if(?useJackson, "\n\t@JsonCreator", "")
    "\n\tpublic static " st:call-template(idstt:to_enum_name, ?class) " fromValue(String text) {"
    "\n\t\tfor ("st:call-template(idstt:to_enum_name, ?class) " b : " st:call-template(idstt:to_enum_name, ?class)".values()) {"
    "\n\t\t\tif (String.valueOf(b.value).equals(text)) {"
    "\n\t\t\t\treturn b;"
    "\n\t\t\t}"
    "\n\t\t}"
    "\n\t\treturn null;"
    "\n\t}"

enum-default-properties.rq

add the member variable "value".

    "\n"
    "\n\tprivate String value;"

process-enum-individuals.rq

use the constructor and add the value, here the template providing the enum's value is called!

"\t" ucase(replace(st:call-template(idstt:to_class_name, ?individual), "([a-z])([A-Z])", CONCAT("$1", "_", "$2")))
"(\""
st:call-template(idstt:to_enum_value, ?individual)
"\"),"

to_enum_value.rq

This template checks whether the parent is DataTypeDefXsd and constructs the respective name, taking into account that the definition is named AnyUri (not AnyURI) and the first character is lowercase ... in all other cases, the name of the class is used.

prefix idstt: <https://w3id.org/idsa/transformationtemplates/>
prefix xsd: <http://www.w3.org/2001/XMLSchema#>
prefix aas: <https://admin-shell.io/aas/3/0/RC02/>
prefix def: <https://admin-shell.io/aas/3/0/RC02/DataTypeDefXsd/>

template idstt:to_enum_value(?class) {

    if ( str(?parent) = str(<aas:DataTypeDefXsd>), 
        if (str(?class) = str(<def:AnyUri>), "xs:anyURI", 
            CONCAT("xs:", LCASE( SUBSTR( ?localName, 1, 1)), SUBSTR( ?localName, 2))
        ),
        st:call-template(idstt:to_class_name, ?class)
    )

}
where {
    ?class rdf:type ?parent . 
    BIND( st:call-template(idstt:to_class_name, ?class) AS ?localName)
}

Mixin instead of EnumSerializer/EnumDeserializer

using a mixin-class for each of the enums does the job. As a result, the EnumSerializer or Deserializer can be omitted

public abstract class AASEnumMixin<T> {
    @JsonValue
    public abstract String toString();
    @JsonCreator
    public abstract T fromValue(String text);
}

AASEnumGeneration.zip

sebbader-sap commented 1 year ago

Hi @dglachs , first of all, really sorry for not answering earlier. It looks like I completely lost track of this issue.

According to the proposed content: Looks great! I'll prepare a feature branch soon and check how it works with the current state of the generator and the V3.0 AAS metamodel version.

(And, by the way, pretty impressive that you were able to go through the templates just by yourself!)