From 2fc03523c64803826e74ce0af42880f65f60a107 Mon Sep 17 00:00:00 2001 From: Michal Cmarada Date: Fri, 8 Feb 2019 12:28:09 +0100 Subject: introduce EnumSets for Flag types - this enables to set multiple enum options for flag types in VPP Change-Id: If7a59f8c3a47f712f6f591e1ce2547b15e0b2afb Signed-off-by: Michal Cmarada --- java/jvpp/gen/jvpp_gen.py | 2 + java/jvpp/gen/jvppgen/enumsets_gen.py | 149 +++++++++++++++++++++++++ java/jvpp/gen/jvppgen/jni_common_gen.py | 12 +- java/jvpp/gen/jvppgen/jni_type_handlers_gen.py | 56 +++++++++- java/jvpp/gen/jvppgen/jvpp_model.py | 45 +++++++- 5 files changed, 260 insertions(+), 4 deletions(-) create mode 100755 java/jvpp/gen/jvppgen/enumsets_gen.py diff --git a/java/jvpp/gen/jvpp_gen.py b/java/jvpp/gen/jvpp_gen.py index 067a92f..d7f386c 100755 --- a/java/jvpp/gen/jvpp_gen.py +++ b/java/jvpp/gen/jvpp_gen.py @@ -20,6 +20,7 @@ import sys from jvppgen.types_gen import generate_types from jvppgen.enums_gen import generate_enums +from jvppgen.enumsets_gen import generate_enumsets from jvppgen.unions_gen import generate_unions from jvppgen.dto_gen import generate_dtos from jvppgen.jvpp_ifc_gen import generate_java_ifc @@ -36,6 +37,7 @@ def generate_jvpp(root_dir, model, logger): base_dir = "%s/target/%s" % (root_dir, model.plugin_package.replace(".", "/")) generate_types(_work_dir(base_dir, "types"), model, logger) generate_enums(_work_dir(base_dir, "types"), model, logger) + generate_enumsets(_work_dir(base_dir, "types"), model, logger) generate_unions(_work_dir(base_dir, "types"), model, logger) generate_dtos(_work_dir(base_dir, "dto"), model, logger) generate_java_ifc(_work_dir(base_dir), model, logger) diff --git a/java/jvpp/gen/jvppgen/enumsets_gen.py b/java/jvpp/gen/jvppgen/enumsets_gen.py new file mode 100755 index 0000000..029c4ec --- /dev/null +++ b/java/jvpp/gen/jvppgen/enumsets_gen.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python2 +# +# Copyright (c) 2019 Cisco and/or its affiliates. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at: +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from string import Template + +from jvpp_model import EnumSet + + +def generate_enumsets(work_dir, model, logger): + logger.debug("Generating enumset for %s " % model.json_api_files) + + for t in model.types: + if not isinstance(t, EnumSet): + continue + logger.debug("Generating DTO for enumset %s", t) + type_class_name = t.java_name + type_class = _ENUM_TEMPLATE.substitute( + plugin_package=model.plugin_package, + c_type_name=t.name, + json_filename=model.json_api_files, + json_definition=t.doc, + java_name=type_class_name, + java_enum_name=type_class_name + "Options", + constants=_generate_constants(t.constants), + value_type=t.value.type.java_name + ) + with open("%s/%s.java" % (work_dir, type_class_name), "w") as f: + f.write(type_class) + + +_ENUM_TEMPLATE = Template(""" +package $plugin_package.types; + +import java.util.EnumSet; + +/** + *

This class represents $c_type_name enumset definition. + *
It was generated by enumsets_gen.py based on $json_filename: + *

+$json_definition
+ * 
+ */ +public final class $java_name { + + private final EnumSet<$java_enum_name> options = EnumSet.noneOf($java_enum_name.class); + + @Override + public boolean equals(final Object obj) { + if (!( obj instanceof $java_name)) { + return false; + } + return options.equals((($java_name)obj).getOptions()); + } + + public EnumSet<$java_enum_name> getOptions() { + return options; + } + + public $value_type getOptionsValue() { + $value_type optionsValue = 0; + for ($java_enum_name opts : options) { + optionsValue = optionsValue | opts.value; + } + return optionsValue; + } + + public void setOptionsValue(int value) { + options.clear(); + + if (value == 0) { + // if value is "0" set default value and exit + options.add($java_enum_name.forValue(0)); + return; + } + + for ($java_enum_name option : $java_enum_name.values()) { + if ((option.value > 0) & ((option.value & value) == option.value)) { + options.add(option); + } + } + } + + public boolean add($java_enum_name option) { + return options.add(option); + } + + public boolean remove($java_enum_name option) { + return options.remove(option); + } + + public boolean removeAll(EnumSet<$java_enum_name> options) { + return options.removeAll(options); + } + + public void clear() { + options.clear(); + } + + public boolean contains($java_enum_name option) { + return options.contains(option); + } + + public boolean containsAll(EnumSet<$java_enum_name> options) { + return options.containsAll(options); + } + + @Override + public java.lang.String toString() { + return "$java_name{" + "options=" + options + '}'; + } + + public enum $java_enum_name { +$constants; + + public final $value_type value; + + $java_enum_name(final $value_type value) { + this.value = value; + } + + public static $java_enum_name forValue(final $value_type value) { + for ($java_enum_name enumeration : $java_enum_name.values()) { + if (value == enumeration.value) { + return enumeration; + } + } + return null; + } + } +} +""") + + +def _generate_constants(constants): + return ",\n".join(_CONSTANT_TEMPLATE.substitute(name=c['name'], value=c['value']) for c in constants) + + +_CONSTANT_TEMPLATE = Template(""" $name($value)""") diff --git a/java/jvpp/gen/jvppgen/jni_common_gen.py b/java/jvpp/gen/jvppgen/jni_common_gen.py index b52e5ff..120241a 100755 --- a/java/jvpp/gen/jvppgen/jni_common_gen.py +++ b/java/jvpp/gen/jvppgen/jni_common_gen.py @@ -15,7 +15,7 @@ # from string import Template -from jvpp_model import is_array, is_retval, Class, Enum, Union +from jvpp_model import is_array, is_retval, Class, Enum, EnumSet, Union def generate_j2c_identifiers(element, class_ref_name, object_ref_name): @@ -432,7 +432,10 @@ def _generate_c2j_primitive_type_swap(msg_java_name, field, object_ref_name, str if field_type.name == "string": template = _C2J_STRING_TYPE_SWAP_TEMPLATE else: - template = _C2J_PRIMITIVE_TYPE_SWAP_TEMPLATE + if isinstance(field_type, EnumSet): + template = _C2J_ENUMSET_TYPE_SWAP_TEMPLATE + else: + template = _C2J_PRIMITIVE_TYPE_SWAP_TEMPLATE else: template = _C2J_ALIAS_PRIMITIVE_TYPE_SWAP_TEMPLATE return template.substitute( @@ -463,6 +466,11 @@ _C2J_ALIAS_PRIMITIVE_TYPE_SWAP_TEMPLATE = Template(""" (*env)->Set${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId, ${net_to_host_function}(*${struct_ref_name})); """) +_C2J_ENUMSET_TYPE_SWAP_TEMPLATE = Template(""" + jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}"); + (*env)->Set${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId, ${net_to_host_function}(env, ${struct_ref_name}->${c_name})); +""") + def _generate_c2j_primitive_type_no_swap(msg_java_name, field, object_ref_name, struct_ref_name): field_type = field.type diff --git a/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py b/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py index d733dd5..657972d 100755 --- a/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py +++ b/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py @@ -16,7 +16,7 @@ from string import Template from jni_common_gen import generate_j2c_swap, generate_j2c_field_swap, generate_j2c_identifiers, generate_c2j_swap -from jvpp_model import Class, Enum, Union +from jvpp_model import Class, Enum, EnumSet, Union def generate_type_handlers(model, logger): @@ -32,6 +32,8 @@ def generate_type_handlers(model, logger): _generate_class(model, t, type_handlers) elif isinstance(t, Enum): _generate_enum(model, t, type_handlers) + elif isinstance(t, EnumSet): + _generate_enumset(model, t, type_handlers) elif isinstance(t, Union): _generate_union(model, t, type_handlers) else: @@ -138,6 +140,58 @@ static inline ${jni_type} _net_to_host_${c_name}(vl_api_${c_name}_t _net) }""") +def _generate_enumset(model, t, type_handlers): + value_type = t.value.type + type_handlers.append(_ENUMSET_NET_TO_HOST_TEMPLATE.substitute( + c_name=t.name, + json_filename=model.json_api_files, + json_definition=t.doc, + class_FQN=t.jni_name, + jni_signature=value_type.jni_signature, + jni_type=value_type.jni_type, + jni_accessor=value_type.jni_accessor, + swap=_generate_scalar_host_to_net_swap(t.value) + )) + + type_handlers.append(_ENUMSET_HOST_TO_NET_TEMPLATE.substitute( + c_name=t.name, + json_filename=model.json_api_files, + json_definition=t.doc, + class_FQN=t.jni_name, + jni_type=value_type.jni_type, + type_swap=_generate_scalar_net_to_host_swap(t.value) + )) + + +_ENUMSET_NET_TO_HOST_TEMPLATE = Template(""" +/** + * Host to network byte order conversion for ${c_name} enum. + * Generated based on $json_filename: +$json_definition + */ +static inline void _host_to_net_${c_name}(JNIEnv * env, jobject _host, vl_api_${c_name}_t * _net) +{ + jclass enumClass = (*env)->FindClass(env, "${class_FQN}"); + jmethodID getValueMethod = (*env)->GetMethodID(env, enumClass, "getOptionsValue", "()I"); + ${jni_type} value = (*env)->CallIntMethod(env, _host, getValueMethod); + ${swap}; +}""") + +_ENUMSET_HOST_TO_NET_TEMPLATE = Template(""" +/** + * Network to host byte order conversion for ${c_name} type. + * Generated based on $json_filename: +$json_definition + */ +static inline jobject _net_to_host_${c_name}(JNIEnv * env, vl_api_${c_name}_t _net) +{ + jclass enumClass = (*env)->FindClass(env, "${class_FQN}"); + jmethodID enumInit = (*env)->GetMethodID(env, enumClass, "setOptionsValue", "(I)V"); + ${jni_type} value = (${jni_type}) $type_swap + return (*env)->NewObject(env, enumClass, enumInit, value); +}""") + + def _generate_scalar_host_to_net_swap(field): field_type = field.type if field_type.is_swap_needed: diff --git a/java/jvpp/gen/jvppgen/jvpp_model.py b/java/jvpp/gen/jvppgen/jvpp_model.py index 9a3204e..c5a7e74 100755 --- a/java/jvpp/gen/jvppgen/jvpp_model.py +++ b/java/jvpp/gen/jvppgen/jvpp_model.py @@ -142,6 +142,34 @@ class Enum(Type): return "_host_to_net_%s(env, %s, &(%s))" % (self.name, host_ref_name, net_ref_name) +class EnumSet(Type): + def __init__(self, name, value, constants, definition, plugin_name): + _java_name = _underscore_to_camelcase_upper(name) + + super(EnumSet, self).__init__( + name=name, + java_name=_java_name, + java_name_fqn="io.fd.vpp.jvpp.%s.types.%s" % (plugin_name, _java_name), + jni_signature="Lio/fd/vpp/jvpp/%s/types/%s;" % (plugin_name, _java_name), + jni_type="jobject", + jni_accessor="Object", + host_to_net_function="_host_to_net_%s" % name, + net_to_host_function="_net_to_host_%s" % name + ) + + self.value = value + self.constants = constants + self.doc = _message_to_javadoc(definition) + self.java_name_lower = _underscore_to_camelcase_lower(name) + self.vpp_name = "%s%s%s" % (_VPP_TYPE_PREFIX, name, _VPP_TYPE_SUFFIX) + # Fully qualified class name used by FindClass function, see: + # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#FindClass + self.jni_name = "io/fd/vpp/jvpp/%s/types/%s" % (plugin_name, _java_name) + + def get_host_to_net_function(self, host_ref_name, net_ref_name): + return "_host_to_net_%s(env, %s, &(%s))" % (self.name, host_ref_name, net_ref_name) + + class Class(Type): def __init__(self, name, crc, fields, definition, plugin_name): _java_name = _underscore_to_camelcase_upper(name) @@ -391,8 +419,10 @@ class JVppModel(object): type = value['type'] data = value['data'][1:] try: - if type == 'enum': + if type == 'enum' and "flags" not in name: type = self._parse_enum(name, data) + elif type == 'enum' and "flags" in name: + type = self._parse_enumset(name, data) elif type == 'union': type = self._parse_union(name, data) elif type == 'type': @@ -468,6 +498,19 @@ class JVppModel(object): raise ParseException("'enumtype' was not defined for %s" % definition) return Enum(name, Field('value', self._types_by_name[type_name]), constants, definition, self.plugin_name) + def _parse_enumset(self, name, definition): + self.logger.debug("Parsing enumset %s: %s", name, definition) + constants = [] + type_name = None + for item in definition: + if type(item) is dict and 'enumtype' in item: + type_name = item['enumtype'] + continue + constants.append({'name': item[0], 'value': item[1]}) + if not type_name: + raise ParseException("'enumtype' was not defined for %s" % definition) + return EnumSet(name, Field('value', self._types_by_name[type_name]), constants, definition, self.plugin_name) + def _parse_union(self, name, definition): self.logger.debug("Parsing union %s: %s", name, definition) crc, fields = self._parse_fields(definition) -- cgit 1.2.3-korg