jvpp: object model for jvpp generator (VPP-1184) 28/10928/2
authorMarek Gradzki <mgradzki@cisco.com>
Fri, 9 Feb 2018 12:42:12 +0000 (13:42 +0100)
committerNeale Ranns <nranns@cisco.com>
Fri, 2 Mar 2018 15:22:34 +0000 (15:22 +0000)
Introduces JSON parser which builds object model of Java API.

Also rewrites JNI translation of typedefs
to use per type translation functions
instead of code inlining.

Not covered:
- integrate with vappigen plugin (VPP-1154) or vapi parser (VPP-1155)
- use better templating engine (VPP-480)
- improvements of generator structure (e.g. VPP-1186)

Change-Id: I9e12d76c2f3c6ee041669f58e8a37917f656aa90
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
19 files changed:
src/vpp-api/java/jvpp/gen/jvpp_gen.py
src/vpp-api/java/jvpp/gen/jvppgen/callback_gen.py [changed mode: 0644->0755]
src/vpp-api/java/jvpp/gen/jvppgen/dto_gen.py [changed mode: 0644->0755]
src/vpp-api/java/jvpp/gen/jvppgen/enum_gen.py [deleted file]
src/vpp-api/java/jvpp/gen/jvppgen/jni_common_gen.py [new file with mode: 0755]
src/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py [changed mode: 0644->0755]
src/vpp-api/java/jvpp/gen/jvppgen/jni_impl_gen.py [new file with mode: 0755]
src/vpp-api/java/jvpp/gen/jvppgen/jni_msg_handlers_gen.py [new file with mode: 0755]
src/vpp-api/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py [new file with mode: 0755]
src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py [deleted file]
src/vpp-api/java/jvpp/gen/jvppgen/jvpp_callback_facade_gen.py
src/vpp-api/java/jvpp/gen/jvppgen/jvpp_common_gen.py [new file with mode: 0755]
src/vpp-api/java/jvpp/gen/jvppgen/jvpp_future_facade_gen.py
src/vpp-api/java/jvpp/gen/jvppgen/jvpp_ifc_gen.py [new file with mode: 0755]
src/vpp-api/java/jvpp/gen/jvppgen/jvpp_impl_gen.py [changed mode: 0644->0755]
src/vpp-api/java/jvpp/gen/jvppgen/jvpp_model.py [new file with mode: 0755]
src/vpp-api/java/jvpp/gen/jvppgen/notification_gen.py
src/vpp-api/java/jvpp/gen/jvppgen/types_gen.py [changed mode: 0644->0755]
src/vpp-api/java/jvpp/gen/jvppgen/util.py [deleted file]

index 2536b4e..a58c580 100755 (executable)
@@ -1,6 +1,6 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2
 #
-# Copyright (c) 2016 Cisco and/or its affiliates.
+# Copyright (c) 2016,2018 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:
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-
 import argparse
-import sys
-import os
-import json
 import logging
+import os
+import sys
 
-from jvppgen import types_gen
-from jvppgen import callback_gen
-from jvppgen import notification_gen
-from jvppgen import dto_gen
-from jvppgen import jvpp_callback_facade_gen
-from jvppgen import jvpp_future_facade_gen
-from jvppgen import jvpp_impl_gen
-from jvppgen import jvpp_c_gen
-from jvppgen import util
-from jvppgen import enum_gen
-
-# Invocation:
-# ~/Projects/vpp/vpp-api/jvpp/gen$ mkdir -p java/io/fd/vpp/jvpp && cd java/io/fd/vpp/jvpp
-# ~/Projects/vpp/vpp-api/jvpp/gen/java/io/fd/vpp/jvpp$ ../../../../jvpp_gen.py -idefs_api_vpp_papi.py
-#
-# Compilation:
-# ~/Projects/vpp/vpp-api/jvpp/gen/java/io/fd/vpp/jvpp$ javac *.java dto/*.java callback/*.java
-#
-# where
-# defs_api_vpp_papi.py - vpe.api in python format (generated by vppapigen)
-
-parser = argparse.ArgumentParser(description='VPP Java API generator')
-parser.add_argument('-i', action="store", dest="inputfiles", nargs='+')
-parser.add_argument('--plugin_name', action="store", dest="plugin_name")
-parser.add_argument('--root_dir', action="store", dest="root_dir")
-args = parser.parse_args()
-
-sys.path.append(".")
-cwd = os.getcwd()
-
-# Initialize logger
-try:
-    verbose = int(os.getenv("V", 0))
-except:
-    verbose = 0
-
-log_level = logging.WARNING
-if verbose == 1:
-    log_level = logging.INFO
-elif verbose >= 2:
-    log_level = logging.DEBUG
-
-logging.basicConfig(stream=sys.stdout, level=log_level)
-logger = logging.getLogger("JVPP GEN")
-logger.setLevel(log_level)
-
-
-logger.info("Generating Java API for %s" % args.inputfiles)
-plugin_name = args.plugin_name
-logger.debug("plugin_name: %s" % plugin_name)
-
-cfg = {}
-
-base_package = 'io.fd.vpp.jvpp'
-plugin_package = base_package + '.' + plugin_name
-root_dir = os.path.abspath(args.root_dir)
-logger.debug("root_dir: %s" % root_dir)
-work_dir = root_dir + "/target/" + plugin_package.replace(".","/")
-
-try:
-    os.makedirs(work_dir)
-except OSError:
-    if not os.path.isdir(work_dir):
-        raise
-
-os.chdir(work_dir)
-
-for inputfile in args.inputfiles:
-    _cfg = json.load(open(cwd + "/" + inputfile, 'r'))
-    if 'enums' in cfg:
-        cfg['enums'].extend(_cfg['enums'])
-    else:
-        cfg['enums'] = _cfg['enums']
-    if 'types' in cfg:
-        cfg['types'].extend(_cfg['types'])
-    else:
-        cfg['types'] = _cfg['types']
-    if 'messages' in cfg:
-        cfg['messages'].extend(_cfg['messages'])
-    else:
-        cfg['messages'] = _cfg['messages']
-
-
-def is_request_field(field_name):
-    return field_name not in {'_vl_msg_id', 'client_index', 'context'}
-
-
-def is_response_field(field_name):
-    return field_name not in {'_vl_msg_id'}
-
-
-def get_args(t, filter):
-    arg_names = []
-    arg_types = []
-    for i in t:
-        if is_crc(i):
-            continue
-        if not filter(i[1]):
-            continue
-        arg_types.append(i[0])
-        arg_names.append(i[1])
-    return arg_types, arg_names
-
-
-def get_types(t, filter):
-    types_list = []
-    lengths_list = []
-    crc = None
-    for i in t:
-        if is_crc(i):
-            crc = ('crc', i['crc'][2:])
-            continue
-        if not filter(i[1]):
-            continue
-        if len(i) is 3:  # array type
-            types_list.append(i[0] + '[]')
-            lengths_list.append((i[2], False))
-        elif len(i) is 4:  # variable length array type
-            types_list.append(i[0] + '[]')
-            lengths_list.append((i[3], True))
-        else:  # primitive type
-            types_list.append(i[0])
-            lengths_list.append((0, False))
-    return types_list, lengths_list, crc
-
-
-def is_crc(arg):
-    """ Check whether the argument inside message definition is just crc """
-    return 'crc' in arg
-
-
-def get_definitions(defs):
-    # Pass 1
-    func_list = []
-    func_name = {}
-    for a in defs:
-        java_name = util.underscore_to_camelcase(a[0])
-
-        # For replies include all the arguments except message_id
-        if util.is_reply(java_name):
-            types, lengths, crc = get_types(a[1:], is_response_field)
-            args = get_args(a[1:], is_response_field)
-            func_name[a[0]] = dict(
-                [('name', a[0]), ('java_name', java_name),
-                 ('args', args[1]), ('arg_types', args[0]),
-                 ('types', types), ('lengths', lengths), crc])
-        # For requests skip message_id, client_id and context
-        else:
-            types, lengths, crc = get_types(a[1:], is_request_field)
-            args = get_args(a[1:], is_request_field)
-            func_name[a[0]] = dict(
-                [('name', a[0]), ('java_name', java_name),
-                 ('args', args[1]), ('arg_types', args[0]),
-                 ('types', types), ('lengths', lengths), crc])
-
-        # Indexed by name
-        func_list.append(func_name[a[0]])
-    return func_list, func_name
-
-
-types_package = 'types'
-dto_package = 'dto'
-callback_package = 'callback'
-notification_package = 'notification'
-future_package = 'future'
-# TODO find better package name
-callback_facade_package = 'callfacade'
-
-types_list, types_name = get_definitions(cfg['types'])
-func_list, func_name = get_definitions(cfg['messages'])
-
-enum_gen.generate_enums(cfg['enums'], args.inputfiles, logger)
-types_gen.generate_types(types_list, plugin_package, types_package, args.inputfiles, logger)
-dto_gen.generate_dtos(func_list, base_package, plugin_package, plugin_name.title(), dto_package, args.inputfiles,
-                      logger)
-jvpp_impl_gen.generate_jvpp(func_list, base_package, plugin_package, plugin_name, dto_package, args.inputfiles, logger)
-callback_gen.generate_callbacks(func_list, base_package, plugin_package, plugin_name.title(), callback_package,
-                                dto_package, args.inputfiles, logger)
-notification_gen.generate_notification_registry(func_list, base_package, plugin_package, plugin_name.title(),
-                                                notification_package, callback_package, dto_package, args.inputfiles,
-                                                logger)
-jvpp_c_gen.generate_jvpp(func_list, plugin_name, args.inputfiles, root_dir, logger)
-jvpp_future_facade_gen.generate_jvpp(func_list, base_package, plugin_package, plugin_name.title(), dto_package,
-                                     callback_package, notification_package, future_package, args.inputfiles, logger)
-jvpp_callback_facade_gen.generate_jvpp(func_list, base_package, plugin_package, plugin_name.title(), dto_package,
-                                       callback_package, notification_package, callback_facade_package, args.inputfiles,
-                                       logger)
-
-logger.info("Java API for %s generated successfully" % args.inputfiles)
+from jvppgen.types_gen import generate_types
+from jvppgen.dto_gen import generate_dtos
+from jvppgen.jvpp_ifc_gen import generate_java_ifc
+from jvppgen.jvpp_impl_gen import generate_java_impl
+from jvppgen.callback_gen import generate_callbacks
+from jvppgen.jni_gen import generate_jni
+from jvppgen.notification_gen import generate_notifications
+from jvppgen.jvpp_future_facade_gen import generate_future_facade
+from jvppgen.jvpp_callback_facade_gen import generate_callback_facade
+from jvppgen.jvpp_model import JVppModel
+
+
+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_dtos(_work_dir(base_dir, "dto"), model, logger)
+    generate_java_ifc(_work_dir(base_dir), model, logger)
+    generate_java_impl(_work_dir(base_dir), model, logger)
+    generate_callbacks(_work_dir(base_dir, "callback"), model, logger)
+    generate_jni(root_dir, model, logger)
+    generate_notifications(_work_dir(base_dir, "notification"), model, logger)
+    generate_future_facade(_work_dir(base_dir, "future"), model, logger)
+    generate_callback_facade(_work_dir(base_dir, "callfacade"), model, logger)
+
+
+def _work_dir(work_dir, sub_dir=None):
+    if sub_dir:
+        work_dir = "%s/%s" % (work_dir, sub_dir)
+    try:
+        os.makedirs(work_dir)
+    except OSError:
+        if not os.path.isdir(work_dir):
+            raise
+    return work_dir
+
+
+def _init_logger():
+    try:
+        verbose = int(os.getenv("V", 0))
+    except:
+        verbose = 0
+
+    log_level = logging.WARNING
+    if verbose == 1:
+        log_level = logging.INFO
+    elif verbose >= 2:
+        log_level = logging.DEBUG
+
+    logging.basicConfig(stream=sys.stdout, level=log_level)
+    logger = logging.getLogger("JVPP GEN")
+    logger.setLevel(log_level)
+    return logger
+
+
+if __name__ == '__main__':
+    logger = _init_logger()
+
+    argparser = argparse.ArgumentParser(description="VPP Java API generator")
+    argparser.add_argument('-i', nargs='+', metavar='api_file.json', help="json vpp api file(s)")
+    argparser.add_argument('--plugin_name')
+    argparser.add_argument('--root_dir')
+    args = argparser.parse_args()
+
+    logger.info("Generating Java API for %s" % args.i)
+    logger.debug("plugin_name: %s" % args.plugin_name)
+    logger.debug("root_dir: %s" % args.root_dir)
+
+    model = JVppModel(logger, args.i, args.plugin_name)
+    generate_jvpp(args.root_dir, model, logger)
old mode 100644 (file)
new mode 100755 (executable)
index 13a4155..14aa8b7
@@ -1,6 +1,6 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2
 #
-# Copyright (c) 2016 Cisco and/or its affiliates.
+# Copyright (c) 2016,2018 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:
 # 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.
-
-import os
-import util
+#
 from string import Template
 
-callback_suffix = "Callback"
+from jvpp_model import is_request, is_dump, is_control_ping, is_control_ping_reply
 
-callback_template = Template("""
-package $plugin_package.$callback_package;
+
+def generate_callbacks(work_dir, model, logger):
+    json_api_files = model.json_api_files
+    logger.debug("Generating Callback interfaces for %s" % json_api_files)
+    plugin_package = model.plugin_package
+
+    callbacks = []
+    for msg in model.messages:
+        name = msg.java_name_upper
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control_ping managed by jvpp registry.
+            continue
+        if is_dump(msg) or is_request(msg):
+            continue
+
+        callbacks.append("%s.callback.%sCallback" % (plugin_package, name))
+        callback = _CALLBACK_TEMPLATE.substitute(
+            plugin_package=plugin_package,
+            json_filename=json_api_files,
+            name=name)
+
+        with open("%s/%sCallback.java" % (work_dir, name), "w") as f:
+            f.write(callback)
+
+    plugin_name = model.plugin_java_name
+    with open("%s/JVpp%sGlobalCallback.java" % (work_dir, plugin_name), "w") as f:
+        f.write(_GLOBAL_CALLBACK_TEMPLATE.substitute(
+            plugin_package=plugin_package,
+            json_filename=json_api_files,
+            plugin_name=plugin_name,
+            callbacks=", ".join(callbacks)
+        ))
+
+_CALLBACK_TEMPLATE = Template("""package $plugin_package.callback;
 
 /**
- * <p>Represents callback for plugin's api file message.
- * <br>It was generated by callback_gen.py based on $inputfile preparsed data:
- * <pre>
-$docs
- * </pre>
+ * <p>Represents callback for plugin's api message.
+ * <br>It was generated by jvpp_callback_gen.py based on $json_filename.
  */
-public interface $cls_name extends $base_package.$callback_package.$callback_type {
-
-    $callback_method
+public interface ${name}Callback extends io.fd.vpp.jvpp.callback.JVppCallback {
 
+    void on${name}(${plugin_package}.dto.${name} reply);
 }
 """)
 
-global_callback_template = Template("""
-package $plugin_package.$callback_package;
+_GLOBAL_CALLBACK_TEMPLATE = Template("""package $plugin_package.callback;
 
 /**
  * <p>Global aggregated callback interface.
- * <br>It was generated by callback_gen.py based on $inputfile
- * <br>(python representation of api file generated by vppapigen).
+ * <br>It was generated by jvpp_callback_gen.py based on $json_filename.
  */
-public interface JVpp${plugin_name}GlobalCallback extends $base_package.$callback_package.ControlPingCallback, $callbacks {
+public interface JVpp${plugin_name}GlobalCallback extends io.fd.vpp.jvpp.callback.ControlPingCallback, $callbacks {
 }
 """)
-
-
-def generate_callbacks(func_list, base_package, plugin_package, plugin_name, callback_package, dto_package, inputfile,
-                       logger):
-    """ Generates callback interfaces """
-    logger.debug("Generating Callback interfaces for %s" % inputfile)
-
-    if not os.path.exists(callback_package):
-        os.mkdir(callback_package)
-
-    callbacks = []
-    for func in func_list:
-        camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name'])
-
-        if util.is_control_ping(camel_case_name_with_suffix):
-            # Skip control_ping managed by jvpp registry.
-            continue
-        if util.is_dump(func['name']) or util.is_request(func['name'], func_list):
-            continue
-
-        # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client).
-        callback_type = "JVppCallback"
-        callbacks.append("{0}.{1}.{2}".format(plugin_package, callback_package, camel_case_name_with_suffix + callback_suffix))
-        callback_path = os.path.join(callback_package, camel_case_name_with_suffix + callback_suffix + ".java")
-        callback_file = open(callback_path, 'w')
-
-        reply_type = "%s.%s.%s" % (plugin_package, dto_package, camel_case_name_with_suffix)
-        method = "void on{0}({1} reply);".format(camel_case_name_with_suffix, reply_type)
-        callback_file.write(
-            callback_template.substitute(inputfile=inputfile,
-                                         docs=util.api_message_to_javadoc(func),
-                                         cls_name=camel_case_name_with_suffix + callback_suffix,
-                                         callback_method=method,
-                                         base_package=base_package,
-                                         plugin_package=plugin_package,
-                                         callback_package=callback_package,
-                                         callback_type=callback_type))
-        callback_file.flush()
-        callback_file.close()
-
-    callback_file = open(os.path.join(callback_package, "JVpp%sGlobalCallback.java" % plugin_name), 'w')
-    callback_file.write(global_callback_template.substitute(inputfile=inputfile,
-                                                            callbacks=", ".join(callbacks),
-                                                            base_package=base_package,
-                                                            plugin_package=plugin_package,
-                                                            plugin_name=plugin_name,
-                                                            callback_package=callback_package))
-    callback_file.flush()
-    callback_file.close()
old mode 100644 (file)
new mode 100755 (executable)
index 035a5b4..ca015a3
@@ -1,6 +1,6 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2
 #
-# Copyright (c) 2016 Cisco and/or its affiliates.
+# Copyright (c) 2016,2018 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:
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import os
 from string import Template
 
-import util
+from jvpp_common_gen import generate_hash_code, generate_equals, generate_to_string, generate_fields
+from jvpp_model import is_request, is_reply, is_retval, is_dump, is_details, is_event, is_control_ping, \
+    is_control_ping_reply
 
-dto_template = Template("""
-package $plugin_package.$dto_package;
 
-/**
- * <p>This class represents $description.
- * <br>It was generated by dto_gen.py based on $inputfile preparsed data:
- * <pre>
-$docs
- * </pre>
- */
-public final class $cls_name implements $base_package.$dto_package.$base_type {
+def generate_dtos(work_dir, model, logger):
+    logger.debug("Generating DTOs for %s " % model.json_api_files)
+    _generate_message_dtos(work_dir, model, logger)
+    _generate_dump_reply_wrappers(work_dir, model, logger)
 
-$fields
-$methods
-}
-""")
 
-dto_template_typeless = Template("""
-package $plugin_package.$dto_package;
+def _generate_message_dtos(work_dir, model, logger):
+    for msg in model.messages:
+        logger.debug("Generating DTO for message %s", msg)
+        class_name = msg.java_name_upper
+
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control_ping managed by jvpp registry.
+            continue
+        if is_request(msg):
+            dto = _generate_request_dto(msg, model, base_type="JVppRequest")
+        elif is_dump(msg):
+            dto = _generate_request_dto(msg, model, base_type="JVppDump")
+        elif is_reply(msg) or is_details(msg):
+            dto = _generate_reply_dto(msg, model)
+        elif is_event(msg):
+            dto = _generate_event_dto(msg, model)
+        else:
+            logger.warn("Failed to generate DTO for: %s. Message type is not supported." % msg)
+            continue
+        with open("%s/%s.java" % (work_dir, class_name), "w") as f:
+            f.write(dto)
+
+
+def _generate_request_dto(msg, model, base_type):
+    msg_java_name_upper = msg.java_name_upper
+    fields = msg.fields
+    return _REQUEST_TEMPLATE.substitute(
+        plugin_package=model.plugin_package,
+        json_filename=model.json_api_files,
+        json_definition=msg.doc,
+        class_name=msg_java_name_upper,
+        base_type=base_type,
+        fields=generate_fields(fields),
+        hash_code=generate_hash_code(fields),
+        equals=generate_equals(msg_java_name_upper, fields),
+        to_string=generate_to_string(msg_java_name_upper, fields),
+        send=_generate_send(model, msg))
+
+_REQUEST_TEMPLATE = Template("""
+package $plugin_package.dto;
 
 /**
- * <p>This class represents $description.
- * <br>It was generated by dto_gen.py based on $inputfile preparsed data:
+ * <p>This class represents request DTO.
+ * <br>It was generated by dto_gen.py based on $json_filename:
  * <pre>
-$docs
+$json_definition
  * </pre>
  */
-public final class $cls_name {
-
+public final class $class_name implements io.fd.vpp.jvpp.dto.$base_type {
 $fields
-$methods
+$hash_code
+$equals
+$to_string
+$send
 }
 """)
 
-field_template = Template("""    public $type $name;\n""")
 
-send_template = Template("""    @Override
-    public int send(final $base_package.JVpp jvpp) throws io.fd.vpp.jvpp.VppInvocationException {
+def _generate_send(model, msg):
+    return _SEND_TEMPLATE.substitute(
+        plugin_package=model.plugin_package,
+        plugin_name=model.plugin_java_name,
+        method_name=msg.java_name_lower,
+        args="this" if msg.has_fields else ""
+    )
+
+_SEND_TEMPLATE = Template("""
+    @Override
+    public int send(final io.fd.vpp.jvpp.JVpp jvpp) throws io.fd.vpp.jvpp.VppInvocationException {
         return (($plugin_package.JVpp${plugin_name})jvpp).$method_name($args);
     }""")
 
 
-def generate_dtos(func_list, base_package, plugin_package, plugin_name, dto_package, inputfile, logger):
-    """ Generates dto objects in a dedicated package """
-    logger.debug("Generating DTOs for %s" % inputfile)
-
-    if not os.path.exists(dto_package):
-        os.mkdir(dto_package)
-
-    for func in func_list:
-        camel_case_dto_name = util.underscore_to_camelcase_upper(func['name'])
-        camel_case_method_name = util.underscore_to_camelcase(func['name'])
-        dto_path = os.path.join(dto_package, camel_case_dto_name + ".java")
-
-        if util.is_control_ping(camel_case_dto_name):
-            continue
-
-        fields = generate_dto_fields(camel_case_dto_name, func)
-        methods = generate_dto_base_methods(camel_case_dto_name, func)
-        base_type = ""
-
-        if util.is_reply(camel_case_dto_name):
-            description = "reply DTO"
-            request_dto_name = util.remove_reply_suffix(camel_case_dto_name)
-            if util.is_details(camel_case_dto_name):
-                base_type += "JVppReply<%s.%s.%s>" % (plugin_package, dto_package, request_dto_name + "Dump")
-                generate_dump_reply_dto(request_dto_name, base_package, plugin_package, dto_package,
-                                        camel_case_dto_name, camel_case_method_name, func)
-            else:
-                base_type += "JVppReply<%s.%s.%s>" % (plugin_package, dto_package, request_dto_name)
-        elif util.is_dump(camel_case_dto_name) or util.is_request(func['name'], func_list):
-            args = "" if fields is "" else "this"
-            methods += send_template.substitute(method_name=camel_case_method_name,
-                                                base_package=base_package,
-                                                plugin_package=plugin_package,
-                                                plugin_name=plugin_name,
-                                                args=args)
-            if util.is_dump(camel_case_dto_name):
-                base_type += "JVppDump"
-                description = "dump request DTO"
-            else:
-                base_type += "JVppRequest"
-                description = "request DTO"
-        else:
-            description = "event DTO"
-            dto_path = os.path.join(dto_package, camel_case_dto_name + ".java")
-
-        write_dto_file(base_package, plugin_package, base_type, camel_case_dto_name, description, dto_package,
-                       dto_path, fields, func, inputfile, methods)
-
-    flush_dump_reply_dtos(inputfile)
-
+def _generate_reply_dto(msg, model):
+    msg_java_name_upper = msg.java_name_upper
+    # Negative retval is mapped to java exception, so filter it out:
+    fields = filter(lambda field: not is_retval(field), msg.fields)
+    return _REPLY_TEMPLATE.substitute(
+        plugin_package=model.plugin_package,
+        json_filename=model.json_api_files,
+        json_definition=msg.doc,
+        class_name=msg_java_name_upper,
+        request_name=msg.request_java,
+        fields=generate_fields(fields),
+        hash_code=generate_hash_code(fields),
+        equals=generate_equals(msg_java_name_upper, fields),
+        to_string=generate_to_string(msg_java_name_upper, fields))
+
+_REPLY_TEMPLATE = Template("""
+package $plugin_package.dto;
 
-def generate_dto_base_methods(camel_case_dto_name, func):
-    methods = generate_dto_hash(func)
-    methods += generate_dto_equals(camel_case_dto_name, func)
-    methods += generate_dto_tostring(camel_case_dto_name, func)
-    return methods
+/**
+ * <p>This class represents reply DTO.
+ * <br>It was generated by jvpp_dto_gen.py based on $json_filename:
+ * <pre>
+$json_definition
+ * </pre>
+ */
+public final class $class_name implements io.fd.vpp.jvpp.dto.JVppReply<$plugin_package.dto.$request_name> {
+$fields
+$hash_code
+$equals
+$to_string
+}
+""")
 
 
-def generate_dto_fields(camel_case_dto_name, func):
-    fields = ""
-    for t in zip(func['types'], func['args']):
-        # for retval don't generate dto field in Reply
-        field_name = util.underscore_to_camelcase(t[1])
-        if util.is_reply(camel_case_dto_name) and util.is_retval_field(field_name):
-            continue
-        fields += field_template.substitute(type=util.jni_2_java_type_mapping[t[0]],
-                                            name=field_name)
-    return fields
+def _generate_event_dto(msg, model):
+    msg_java_name_upper = msg.java_name_upper
+    # Negative retval is mapped to java exception, so filter it out:
+    fields = filter(lambda field: not is_retval(field), msg.fields)
+    return _EVENT_TEMPLATE.substitute(
+        plugin_package=model.plugin_package,
+        json_filename=model.json_api_files,
+        json_definition=msg.doc,
+        class_name=msg_java_name_upper,
+        fields=generate_fields(fields),
+        hash_code=generate_hash_code(fields),
+        equals=generate_equals(msg_java_name_upper, fields),
+        to_string=generate_to_string(msg_java_name_upper, fields))
 
+_EVENT_TEMPLATE = Template("""
+package $plugin_package.dto;
 
-tostring_field_template = Template("""                \"$field_name=\" + $field_name + ", " +\n""")
-tostring_array_field_template = Template("""                \"$field_name=\" + java.util.Arrays.toString($field_name) + ", " +\n""")
-tostring_template = Template("""    @Override
-    public String toString() {
-        return "$cls_name{" +
-$fields_tostring "}";
-    }\n\n""")
+/**
+ * <p>This class represents event DTO.
+ * <br>It was generated by jvpp_dto_gen.py based on $json_filename:
+ * <pre>
+$json_definition
+ * </pre>
+ */
+public final class $class_name {
+$fields
+$hash_code
+$equals
+$to_string
+}
+""")
 
 
-def generate_dto_tostring(camel_case_dto_name, func):
-    tostring_fields = ""
-    for t in zip(func['types'], func['args']):
+def _generate_dump_reply_wrappers(work_dir, model, logger):
+    for msg in model.messages:
+        if is_details(msg):
+            logger.debug("Generating ReplyDump DTO for message %s", msg)
+            details_class = msg.java_name_upper
+            dto = _REPLY_DUMP_TEMPLATE.substitute(
+                plugin_package=model.plugin_package,
+                json_filename=model.json_api_files,
+                json_definition=msg.doc,
+                details_class=details_class,
+                details_field=msg.java_name_lower,
+                dump_class=msg.request_java
+            )
+            with open("%s/%sReplyDump.java" % (work_dir, details_class), "w") as f:
+                f.write(dto)
+
+_REPLY_DUMP_TEMPLATE = Template("""
+package $plugin_package.dto;
 
-        field_name = util.underscore_to_camelcase(t[1])
-        # for retval don't generate dto field in Reply
-        if util.is_retval_field(field_name):
-            continue
+/**
+ * <p>This class represents dump reply wrapper.
+ * <br>It was generated by jvpp_dto_gen.py based on $json_filename:
+ * <pre>
+$json_definition
+ * </pre>
+ */
+public final class ${details_class}ReplyDump implements io.fd.vpp.jvpp.dto.JVppReplyDump<${plugin_package}.dto.${dump_class}, ${plugin_package}.dto.${details_class}> {
 
-        # handle array types
-        if util.is_array(util.jni_2_java_type_mapping[t[0]]):
-            tostring_fields += tostring_array_field_template.substitute(field_name=field_name)
-        else:
-            tostring_fields += tostring_field_template.substitute(field_name=field_name)
+    public java.util.List<${details_class}> ${details_field} = new java.util.ArrayList<>();
 
-    return tostring_template.substitute(cls_name=camel_case_dto_name,
-                                        fields_tostring=tostring_fields[:-8])
+    @Override
+    @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
+    public int hashCode() {
+        return java.util.Objects.hash(${details_field});
+    }
 
-equals_other_template = Template("""
-        final $cls_name other = ($cls_name) o;
-\n""")
-equals_field_template = Template("""        if (!java.util.Objects.equals(this.$field_name, other.$field_name)) {
-            return false;
-        }\n""")
-equals_array_field_template = Template("""        if (!java.util.Arrays.equals(this.$field_name, other.$field_name)) {
-            return false;
-        }\n""")
-equals_template = Template("""    @Override
+    @Override
     public boolean equals(final Object o) {
         if (this == o) {
             return true;
@@ -174,147 +210,22 @@ equals_template = Template("""    @Override
         if (o == null || getClass() != o.getClass()) {
             return false;
         }
-$comparisons
-        return true;
-    }\n\n""")
-
-
-def generate_dto_equals(camel_case_dto_name, func):
-    equals_fields = ""
-    for t in zip(func['types'], func['args']):
-        field_name = util.underscore_to_camelcase(t[1])
-        # for retval don't generate dto field in Reply
-        if util.is_retval_field(field_name):
-            continue
-
-        # handle array types
-        if util.is_array(util.jni_2_java_type_mapping[t[0]]):
-            equals_fields += equals_array_field_template.substitute(field_name=field_name)
-        else:
-            equals_fields += equals_field_template.substitute(field_name=field_name)
-
-    if equals_fields != "":
-        equals_fields = equals_other_template.substitute(cls_name=camel_case_dto_name) + equals_fields
-
-    return equals_template.substitute(comparisons=equals_fields)
 
+        final ${details_class}ReplyDump other = (${details_class}ReplyDump) o;
 
-hash_template = Template("""    @Override
-    @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
-    public int hashCode() {
-        return java.util.Objects.hash($fields);
-    }\n\n""")
-hash_single_array_type_template = Template("""    @Override
-    @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
-    public int hashCode() {
-        return java.util.Arrays.hashCode($fields);
-    }\n\n""")
-
+        if (!java.util.Objects.equals(this.${details_field}, other.${details_field})) {
+            return false;
+        }
 
-def generate_dto_hash(func):
-    hash_fields = ""
+        return true;
+    }
 
-    # Special handling for hashCode in case just a single array field is present. Cannot use Objects.equals since the
-    # array is mistaken for a varargs parameter. Instead use Arrays.hashCode in such case.
-    if len(func['args']) == 1:
-        single_type = func['types'][0]
-        single_type_name = func['args'][0]
-        if util.is_array(util.jni_2_java_type_mapping[single_type]):
-            return hash_single_array_type_template.substitute(fields=util.underscore_to_camelcase(single_type_name))
+    @Override
+    public String toString() {
+        return "${details_class}ReplyDump{" +
+                "${details_field}=" + ${details_field} + "}";
+    }
 
-    for t in zip(func['types'], func['args']):
-        field_name = util.underscore_to_camelcase(t[1])
-        # for retval don't generate dto field in Reply
-        if util.is_retval_field(field_name):
-            continue
 
-        hash_fields += field_name + ", "
-
-    return hash_template.substitute(fields=hash_fields[:-2])
-
-
-def write_dto_file(base_package, plugin_package, base_type, camel_case_dto_name, description, dto_package, dto_path,
-                   fields, func, inputfile, methods):
-    dto_file = open(dto_path, 'w')
-    if base_type != "":
-        dto_file.write(dto_template.substitute(inputfile=inputfile,
-                                               description=description,
-                                               docs=util.api_message_to_javadoc(func),
-                                               cls_name=camel_case_dto_name,
-                                               fields=fields,
-                                               methods=methods,
-                                               base_package=base_package,
-                                               plugin_package=plugin_package,
-                                               base_type=base_type,
-                                               dto_package=dto_package))
-    else:
-        dto_file.write(dto_template_typeless.substitute(inputfile=inputfile,
-                                                        description=description,
-                                                        docs=util.api_message_to_javadoc(func),
-                                                        cls_name=camel_case_dto_name,
-                                                        fields=fields,
-                                                        methods=methods,
-                                                        plugin_package=plugin_package,
-                                                        dto_package=dto_package))
-    dto_file.flush()
-    dto_file.close()
-
-
-dump_dto_suffix = "ReplyDump"
-dump_reply_artificial_dtos = {}
-
-
-def flush_dump_reply_dtos(inputfile):
-    for dump_reply_artificial_dto in dump_reply_artificial_dtos.values():
-        dto_path = os.path.join(dump_reply_artificial_dto['dto_package'],
-                                dump_reply_artificial_dto['cls_name'] + ".java")
-        dto_file = open(dto_path, 'w')
-        dto_file.write(dto_template.substitute(inputfile=inputfile,
-                                               description="dump reply wrapper",
-                                               docs=dump_reply_artificial_dto['docs'],
-                                               cls_name=dump_reply_artificial_dto['cls_name'],
-                                               fields=dump_reply_artificial_dto['fields'],
-                                               methods=dump_reply_artificial_dto['methods'],
-                                               plugin_package=dump_reply_artificial_dto['plugin_package'],
-                                               base_package=dump_reply_artificial_dto['base_package'],
-                                               base_type=dump_reply_artificial_dto['base_type'],
-                                               dto_package=dump_reply_artificial_dto['dto_package']))
-        dto_file.flush()
-        dto_file.close()
-
-
-def generate_dump_reply_dto(request_dto_name, base_package, plugin_package, dto_package, camel_case_dto_name,
-                            camel_case_method_name, func):
-    base_type = "JVppReplyDump<%s.%s.%s, %s.%s.%s>" % (
-        plugin_package, dto_package, util.remove_reply_suffix(camel_case_dto_name) + "Dump",
-        plugin_package, dto_package, camel_case_dto_name)
-    fields = "    public java.util.List<%s> %s = new java.util.ArrayList<>();" % (camel_case_dto_name, camel_case_method_name)
-    cls_name = camel_case_dto_name + dump_dto_suffix
-    # using artificial type for fields, just to bypass the is_array check in base methods generators
-    # the type is not really used
-    artificial_type = 'u8'
-
-    # In case of already existing artificial reply dump DTO, just update it
-    # Used for sub-dump dtos
-    if request_dto_name in dump_reply_artificial_dtos.keys():
-        dump_reply_artificial_dtos[request_dto_name]['fields'] += '\n' + fields
-        dump_reply_artificial_dtos[request_dto_name]['field_names'].append(func['name'])
-        dump_reply_artificial_dtos[request_dto_name]['field_types'].append(artificial_type)
-        methods = '\n' + generate_dto_base_methods(dump_reply_artificial_dtos[request_dto_name]['cls_name'],
-                                            {'args': dump_reply_artificial_dtos[request_dto_name]['field_names'],
-                                             'types': dump_reply_artificial_dtos[request_dto_name]['field_types']})
-        dump_reply_artificial_dtos[request_dto_name]['methods'] = methods
-    else:
-        methods = '\n' + generate_dto_base_methods(cls_name, {'args': [func['name']],
-                                                              'types': [artificial_type]})
-        dump_reply_artificial_dtos[request_dto_name] = ({'docs': util.api_message_to_javadoc(func),
-                                                         'cls_name': cls_name,
-                                                         'fields': fields,
-                                                         'field_names': [func['name']],
-                                                         'field_types': [artificial_type],
-                                                         # strip too many newlines at the end of base method block
-                                                         'methods': methods,
-                                                         'plugin_package': plugin_package,
-                                                         'base_package': base_package,
-                                                         'base_type': base_type,
-                                                         'dto_package': dto_package})
+}
+""")
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/enum_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/enum_gen.py
deleted file mode 100644 (file)
index 51a74e6..0000000
+++ /dev/null
@@ -1,69 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (c) 2018 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.
-
-import os
-import util
-import jni_gen
-
-
-def generate_enums(enum_list, inputfile, logger):
-    """
-    Generates Java representation of enum types defined in the provided JSON file.
-    """
-
-    if not enum_list:
-        logger.debug("Skipping enum generation (%s does not define enum types)." % inputfile)
-        return
-
-    logger.debug("Generating enums for %s" % inputfile)
-
-    for enum in enum_list:
-        enum_name = None
-        enum_type = None
-        for e in enum:
-            if isinstance(e, basestring):
-                enum_name = e
-            elif type(e) is dict and 'enumtype' in e:
-                enum_type = e['enumtype']
-
-        if not enum_name:
-            logger.warn("%s enum is missing name. Skipping" % enum)
-            continue
-
-        if not enum_type:
-            logger.warn("%s enum is missing value type. Skipping" % enum)
-            continue
-
-        # TODO(VPP-1153): add real enum support.
-        # But first refactor java api generation
-        # (either VPP-1154 or VPP-1155, see also VPP-480).
-
-        # As a workaround we just update all the mappings
-        # used for VPP API definitions to JAVA and C/JNI translation.
-        enum_array_type = enum_type + "[]"
-        type_name = "vl_api_" + enum_name + "_t"
-        array_type_name = type_name + "[]"
-        util.vpp_2_jni_type_mapping[type_name] = util.vpp_2_jni_type_mapping[enum_type]
-        util.vpp_2_jni_type_mapping[array_type_name] = util.vpp_2_jni_type_mapping[enum_array_type]
-        util.jni_2_java_type_mapping[type_name] = util.jni_2_java_type_mapping[enum_type]
-        util.jni_2_java_type_mapping[array_type_name] = util.jni_2_java_type_mapping[enum_array_type]
-        util.jni_2_signature_mapping[type_name] = util.jni_2_signature_mapping[enum_type]
-        util.jni_2_signature_mapping[array_type_name] = util.jni_2_signature_mapping[enum_array_type]
-        util.jni_field_accessors[type_name] = util.jni_field_accessors[enum_type]
-        util.jni_field_accessors[array_type_name] = util.jni_field_accessors[enum_array_type]
-        jni_gen.struct_setter_templates[type_name] = jni_gen.struct_setter_templates[enum_type]
-        jni_gen.struct_setter_templates[array_type_name] = jni_gen.struct_setter_templates[enum_array_type]
-        jni_gen.dto_field_setter_templates[type_name] = jni_gen.dto_field_setter_templates[enum_type]
-        jni_gen.dto_field_setter_templates[array_type_name] = jni_gen.dto_field_setter_templates[enum_array_type]
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jni_common_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jni_common_gen.py
new file mode 100755 (executable)
index 0000000..6f2995d
--- /dev/null
@@ -0,0 +1,267 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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 is_array, Class, is_retval
+
+
+def generate_j2c_identifiers(element, class_ref_name, object_ref_name):
+    identifiers = []
+    for field in element.fields:
+        field_type = field.type
+        identifiers.append(_REQUEST_FIELD_IDENTIFIER_TEMPLATE.substitute(
+            java_name=field.java_name,
+            class_ref_name=class_ref_name,
+            jni_signature=field_type.jni_signature,
+            jni_type=field_type.jni_type,
+            jni_accessor=field_type.jni_accessor,
+            object_ref_name=object_ref_name
+        ))
+    return "".join(identifiers)
+
+_REQUEST_FIELD_IDENTIFIER_TEMPLATE = Template("""
+    jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}, "${java_name}", "${jni_signature}");
+    ${jni_type} ${java_name} = (*env)->Get${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId);
+""")
+
+
+# TODO(VPP-1185): introduce host_to_net functions wrappers and simplify j2c and c2j swap generation
+def generate_j2c_swap(element, struct_ref_name):
+    initialization = []
+    for field in element.fields:
+        field_type = field.type
+        if is_array(field):
+            template = _ARRAY_J2C_NO_SWAP_TEMPLATE
+            field_reference_name = field.java_name
+            c_name = field.name
+            swap_elements = None
+            jni_name = None
+            if field_type.is_swap_needed:
+                template = _ARRAY_J2C_SWAP_TEMPLATE
+                host = "%sArrayElements[_i]" % field_reference_name
+                net = "%s->%s[_i]" % (struct_ref_name, c_name)
+                swap_elements = field_type.get_host_to_net_function(host, net)
+            if isinstance(field_type.base_type, Class):
+                jni_name = field_type.base_type.jni_name
+                host = "%sArrayElement" % field_reference_name
+                net = "%s->%s[_i]" % (struct_ref_name, c_name)
+                swap_elements = field_type.get_host_to_net_function(host, net)
+                template = _CLASS_ARRAY_J2C_TEMPLATE
+
+            initialization.append(template.substitute(
+                field_reference_name=field_reference_name,
+                field_length_check=_generate_field_length_check(field),
+                base_type=field_type.base_type.jni_accessor,
+                jni_base_type=field_type.base_type.jni_type,
+                struct_reference_name=struct_ref_name,
+                jni_name=jni_name,
+                c_name=c_name,
+                swap_elements=swap_elements
+            ))
+        else:
+            if field_type.is_swap_needed:
+                host = field.java_name
+                net = "%s->%s" % (struct_ref_name, field.name)
+                initialization.append("    %s;" % field_type.get_host_to_net_function(host, net))
+            else:
+                initialization.append("    %s->%s = %s;" % (struct_ref_name, field.name, field.java_name))
+
+    return "\n".join(initialization)
+
+_ARRAY_J2C_NO_SWAP_TEMPLATE = Template("""
+    if (${field_reference_name}) {
+        jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
+        ${field_length_check}
+        (*env)->Get${base_type}ArrayRegion(env, ${field_reference_name}, 0, cnt, (${jni_base_type} *)${struct_reference_name}->${c_name});
+    }
+""")
+
+_ARRAY_J2C_SWAP_TEMPLATE = Template("""
+    if (${field_reference_name}) {
+        ${jni_base_type} * ${field_reference_name}ArrayElements = (*env)->Get${base_type}ArrayElements(env, ${field_reference_name}, NULL);
+        size_t _i;
+        jsize cnt = (*env)->GetArrayLength(env, ${field_reference_name});
+        ${field_length_check}
+        for (_i = 0; _i < cnt; _i++) {
+            ${swap_elements};
+        }
+        (*env)->Release${base_type}ArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
+    }
+    """)
+
+_CLASS_ARRAY_J2C_TEMPLATE = Template("""
+    {
+        if (${field_reference_name}) {
+            size_t _i;
+            jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
+            ${field_length_check}
+            for (_i = 0; _i < cnt; _i++) {
+                jobject ${field_reference_name}ArrayElement = (*env)->GetObjectArrayElement(env, ${field_reference_name}, _i);
+                ${swap_elements};
+            }
+        }
+    }
+""")
+
+
+def _generate_field_length_check(field):
+    # Enforce max length if array has fixed length or uses variable length syntax
+    field_length = str(field.array_len)
+    if field.array_len_field:
+        field_length = field.array_len_field.java_name
+
+    # TODO: remove when ZLAs without length field are disabled
+    if field_length != "0":
+        return _FIELD_LENGTH_CHECK.substitute(field_length=field_length)
+    else:
+        return ""
+
+# Make sure we do not write more elements that are expected
+_FIELD_LENGTH_CHECK = Template("""
+        size_t max_size = ${field_length};
+        if (cnt > max_size) cnt = max_size;""")
+
+
+def generate_c2j_swap(element, object_ref_name, struct_ref_name):
+    msg_java_name = element.java_name_lower
+    setters = []
+    for field in element.fields:
+        field_type = field.type
+        if is_retval(field):
+            # For retval don't generate setters and generate retval check
+            continue
+        elif is_array(field):
+            jni_name = ""
+            template = _ARRAY_C2J_SWAP_TEMPLATE if field_type.is_swap_needed else _ARRAY_C2J_NO_SWAP_TEMPLATE
+            if isinstance(field_type.base_type, Class):
+                template = _ARRAY_C2J_CLASS_SWAP_TEMPLATE
+                jni_name = field_type.base_type.jni_name
+            setters.append(template.substitute(
+                field_reference_name=field.java_name,
+                class_ref_name=msg_java_name,
+                jni_signature=field_type.jni_signature,
+                jni_type=field_type.jni_type,
+                jni_name=jni_name,
+                base_type=field_type.base_type.jni_accessor,
+                field_length=_generate_array_length(field, struct_ref_name),
+                jni_base_type=field_type.base_type.jni_type,
+                object_ref_name=object_ref_name,
+                struct_ref_name=struct_ref_name,
+                net_to_host_function=field_type.net_to_host_function,
+                c_name=field.name
+            ))
+        else:
+            if field_type.is_swap_needed:
+                template = _SIMPLE_TYPE_FIELD_SETTER_TEMPLATE
+                jni_name = ""
+                if isinstance(field_type, Class):
+                    template = _STRUCT_SETTER_TEMPLATE
+                    jni_name = field_type.jni_name
+                setters.append(template.substitute(
+                    java_name=field.java_name,
+                    class_ref_name=msg_java_name,
+                    jni_signature=field_type.jni_signature,
+                    jni_name=jni_name,
+                    jni_accessor=field_type.jni_accessor,
+                    object_ref_name=object_ref_name,
+                    struct_ref_name=struct_ref_name,
+                    net_to_host_function=field_type.net_to_host_function,
+                    c_name=field.name
+                ))
+            else:
+                setters.append(_SIMPLE_TYPE_NO_SWAP_FIELD_SETTER_TEMPLATE.substitute(
+                    java_name=field.java_name,
+                    class_ref_name=msg_java_name,
+                    jni_signature=field_type.jni_signature,
+                    jni_accessor=field_type.jni_accessor,
+                    object_ref_name=object_ref_name,
+                    struct_ref_name=struct_ref_name,
+                    c_name=field.name
+                ))
+    return "".join(setters)
+
+
+_SIMPLE_TYPE_FIELD_SETTER_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}(${struct_ref_name}->${c_name}));
+""")
+
+_STRUCT_SETTER_TEMPLATE = Template("""
+    jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");
+    jclass ${java_name}Class = (*env)->FindClass(env, "${jni_name}");
+    jmethodID ${java_name}Constructor = (*env)->GetMethodID(env, ${java_name}Class, "<init>", "()V");
+    jobject ${java_name} = (*env)->NewObject(env, ${java_name}Class,  ${java_name}Constructor);
+    ${net_to_host_function}(env, &(${struct_ref_name}->${c_name}), ${java_name});
+    (*env)->Set${jni_accessor}Field(env, ${object_ref_name}, ${java_name}FieldId, ${java_name});
+    (*env)->DeleteLocalRef(env, ${java_name});
+""")
+
+_SIMPLE_TYPE_NO_SWAP_FIELD_SETTER_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, ${struct_ref_name}->${c_name});
+""")
+
+_ARRAY_C2J_NO_SWAP_TEMPLATE = Template("""
+    jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}");
+    ${jni_type} ${field_reference_name} = (*env)->New${base_type}Array(env, ${field_length});
+    (*env)->Set${base_type}ArrayRegion(env, ${field_reference_name}, 0, ${field_length}, (const ${jni_base_type}*)${struct_ref_name}->${c_name});
+    (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name});
+    (*env)->DeleteLocalRef(env, ${field_reference_name});
+""")
+
+_ARRAY_C2J_SWAP_TEMPLATE = Template("""
+    jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}");
+    {
+        ${jni_type} ${field_reference_name} = (*env)->New${base_type}Array(env, ${field_length});
+        ${jni_base_type} * ${field_reference_name}ArrayElements = (*env)->Get${base_type}ArrayElements(env, ${field_reference_name}, NULL);
+        unsigned int _i;
+        for (_i = 0; _i < ${field_length}; _i++) {
+            ${field_reference_name}ArrayElements[_i] = ${net_to_host_function}(${struct_ref_name}->${c_name}[_i]);
+        }
+
+        (*env)->Release${base_type}ArrayElements(env,  ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
+        (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name});
+        (*env)->DeleteLocalRef(env, ${field_reference_name});
+    }
+""")
+
+_ARRAY_C2J_CLASS_SWAP_TEMPLATE = Template("""
+    jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_reference_name}", "${jni_signature}");
+    {
+        jclass ${field_reference_name}Class = (*env)->FindClass(env, "${jni_name}");
+        jobjectArray ${field_reference_name} = (*env)->NewObjectArray(env, ${field_length}, ${field_reference_name}Class, 0);
+        jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "<init>", "()V");
+        unsigned int _i;
+        for (_i = 0; _i < ${field_length}; _i++) {
+            jobject ${field_reference_name}ArrayElement = (*env)->NewObject(env, ${field_reference_name}Class,  ${field_reference_name}Constructor);
+            ${net_to_host_function}(env, &(${struct_ref_name}->${c_name}[_i]), ${field_reference_name}ArrayElement);
+            (*env)->SetObjectArrayElement(env, ${field_reference_name}, _i, ${field_reference_name}ArrayElement);
+            (*env)->DeleteLocalRef(env, ${field_reference_name}ArrayElement);
+        }
+        (*env)->SetObjectField(env, ${object_ref_name}, ${field_reference_name}FieldId, ${field_reference_name});
+        (*env)->DeleteLocalRef(env, ${field_reference_name});
+    }
+""")
+
+
+def _generate_array_length(field, struct_ref_name):
+    if field.array_len_field:
+        len_field = field.array_len_field
+        if len_field.type.is_swap_needed:
+            return "%s(%s->%s)" % (len_field.type.host_to_net_function, struct_ref_name, len_field.name)
+        else:
+            return "%s->%s" % (struct_ref_name, len_field.name)
+    return field.array_len
old mode 100644 (file)
new mode 100755 (executable)
index 8c8be5b..a20a90d
@@ -1,6 +1,6 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2
 #
-# Copyright (c) 2016 Cisco and/or its affiliates.
+# Copyright (c) 2016,2018 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:
 # 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
 
-import util
-
-variable_length_array_value_template = Template("""mp->${length_var_name}""")
-variable_length_array_template = Template("""clib_net_to_host_${length_field_type}(${value})""")
+from jni_impl_gen import generate_jni_impl
+from jni_msg_handlers_gen import generate_jni_handlers
+from jni_type_handlers_gen import generate_type_handlers
+from jvpp_model import is_control_ping, is_dump, is_request, is_control_ping_reply
 
-dto_field_id_template = Template("""
-    jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${field_name}", "${jni_signature}");""")
 
-default_dto_field_setter_template = Template("""
-    (*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, mp->${c_name});
-""")
-
-u16_dto_field_setter_template = Template("""
-    (*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, clib_net_to_host_u16(mp->${c_name}));
-""")
+def generate_jni(work_dir, model, logger):
+    logger.debug("Generating jvpp C for %s" % model.json_api_files)
+    plugin_name = model.plugin_name
+    messages = model.messages
 
-u32_dto_field_setter_template = Template("""
-    (*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, clib_net_to_host_u32(mp->${c_name}));
-""")
+    with open("%s/jvpp_%s_gen.h" % (work_dir, plugin_name), "w") as f:
+        f.write(_JVPP_C_TEMPLATE.substitute(
+            json_filename=model.json_api_files,
+            class_cache=_generate_class_cache(plugin_name, messages),
+            api_verification=_generate_api_verification(messages),
+            type_handlers=generate_type_handlers(model),
+            jni_implementations=generate_jni_impl(model),
+            msg_handlers=generate_jni_handlers(model),
+            handler_registration=_generate_handler_registration(messages)))
 
-u64_dto_field_setter_template = Template("""
-    (*env)->Set${jni_setter}(env, ${object_name}, ${field_reference_name}FieldId, clib_net_to_host_u64(mp->${c_name}));
-""")
+_JVPP_C_TEMPLATE = Template("""/**
+ * This file contains JNI bindings for jvpp Java API.
+ * It was generated by jvpp_jni_gen.py based on $json_filename.
+ */
+$class_cache
 
-u8_array_dto_field_setter_template = Template("""
-    jbyteArray ${field_reference_name} = (*env)->NewByteArray(env, ${field_length});
-    (*env)->SetByteArrayRegion(env, ${field_reference_name}, 0, ${field_length}, (const jbyte*)mp->${c_name});
-    (*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name});
-    (*env)->DeleteLocalRef(env, ${field_reference_name});
-""")
+$api_verification
 
-u16_array_dto_field_setter_template = Template("""
-    {
-        jshortArray ${field_reference_name} = (*env)->NewShortArray(env, ${field_length});
-        jshort * ${field_reference_name}ArrayElements = (*env)->GetShortArrayElements(env, ${field_reference_name}, NULL);
-        unsigned int _i;
-        for (_i = 0; _i < ${field_length}; _i++) {
-            ${field_reference_name}ArrayElements[_i] = clib_net_to_host_u16(mp->${c_name}[_i]);
-        }
+// Type handlers
+$type_handlers
 
-        (*env)->ReleaseShortArrayElements(env,  ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
-        (*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name});
-        (*env)->DeleteLocalRef(env, ${field_reference_name});
-    }
-""")
+// JNI bindings
+$jni_implementations
 
-u32_array_dto_field_setter_template = Template("""
-    {
-        jintArray ${field_reference_name} = (*env)->NewIntArray(env, ${field_length});
-        jint * ${field_reference_name}ArrayElements = (*env)->GetIntArrayElements(env, ${field_reference_name}, NULL);
-        unsigned int _i;
-        for (_i = 0; _i < ${field_length}; _i++) {
-            ${field_reference_name}ArrayElements[_i] = clib_net_to_host_u32(mp->${c_name}[_i]);
-        }
+// Message handlers
+$msg_handlers
 
-        (*env)->ReleaseIntArrayElements(env,  ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
-        (*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name});
-        (*env)->DeleteLocalRef(env, ${field_reference_name});
-    }
+$handler_registration
 """)
 
-# For each u64 array we get its elements. Then we convert values to host byte order.
-# All changes to  jlong* buffer are written to jlongArray (isCopy is set to NULL)
-u64_array_dto_field_setter_template = Template("""
-    {
-        jlongArray ${field_reference_name} = (*env)->NewLongArray(env, ${field_length});
-        jlong * ${field_reference_name}ArrayElements = (*env)->GetLongArrayElements(env, ${field_reference_name}, NULL);
-        unsigned int _i;
-        for (_i = 0; _i < ${field_length}; _i++) {
-            ${field_reference_name}ArrayElements[_i] = clib_net_to_host_u64(mp->${c_name}[_i]);
-        }
 
-        (*env)->ReleaseLongArrayElements(env,  ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
-        (*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name});
-        (*env)->DeleteLocalRef(env, ${field_reference_name});
-    }
+def _generate_class_cache(plugin_name, messages):
+    references = []
+    for msg in messages:
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control_ping managed by jvpp registry.
+            continue
+        references.append((
+            msg.java_name_lower,
+            'io/fd/vpp/jvpp/%s/dto/%s' % (plugin_name, msg.java_name_upper)
+        ))
+
+    references.append(('callbackException', 'io/fd/vpp/jvpp/VppCallbackException'))
+
+    return _CLASS_CACHE_TEMPLATE.substitute(
+        class_references=_generate_class_references(references),
+        create_references=_generate_create_references(references),
+        delete_references=_generate_delete_references(references)
+    )
+
+_CLASS_CACHE_TEMPLATE = Template("""
+// JAVA class reference cache
+$class_references
+
+static int cache_class_references(JNIEnv* env) {
+$create_references
+    return 0;
+}
+
+static void delete_class_references(JNIEnv* env) {
+$delete_references
+}""")
+
+
+def _generate_class_references(references):
+    return "\n".join("jclass %sClass;" % r[0] for r in references)
+
+
+def _generate_create_references(references):
+    items = []
+    for r in references:
+        items.append(_CREATE_GLOBAL_REF_TEMPLATE.substitute(
+            ref_name=r[0],
+            fqn_name=r[1]
+        ))
+    return "".join(items)
+
+_CREATE_GLOBAL_REF_TEMPLATE = Template("""
+    ${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "${fqn_name}"));
+    if ((*env)->ExceptionCheck(env)) {
+        (*env)->ExceptionDescribe(env);
+        return JNI_ERR;
+    }""")
+
+
+def _generate_delete_references(references):
+    items = []
+    for r in references:
+        items.append(_DELETE_CLASS_INVOCATION_TEMPLATE.substitute(ref_name=r[0]))
+    return "".join(items)
+
+_DELETE_CLASS_INVOCATION_TEMPLATE = Template("""
+    if (${ref_name}Class) {
+        (*env)->DeleteGlobalRef(env, ${ref_name}Class);
+    }""")
+
+
+def _generate_api_verification(messages):
+    items = []
+    for msg in messages:
+        items.append("_(%s_%s) \\" % (msg.name, msg.crc))
+    return _API_VERIFICATION_TEMPLATE.substitute(messages="\n".join(items))
+
+_API_VERIFICATION_TEMPLATE = Template("""
+// List of supported API messages used for verification
+#define foreach_supported_api_message \\
+$messages
 """)
 
-dto_field_setter_templates = {'u8': default_dto_field_setter_template,
-                              'u16': u16_dto_field_setter_template,
-                              'u32': u32_dto_field_setter_template,
-                              'i32': u32_dto_field_setter_template,
-                              'u64': u64_dto_field_setter_template,
-                              'f64': default_dto_field_setter_template,  # fixme
-                              'u8[]': u8_array_dto_field_setter_template,
-                              'u16[]': u16_array_dto_field_setter_template,
-                              'u32[]': u32_array_dto_field_setter_template,
-                              'u64[]': u64_array_dto_field_setter_template
-                              }
-
 
-def jni_reply_handler_for_type(handler_name, ref_name, field_type, c_name, field_reference_name,
-                               field_name, field_length, is_variable_len_array, length_field_type,
-                               object_name="dto"):
+def _generate_handler_registration(messages):
     """
-    Generates jni code that initializes a field of java object (dto or custom type).
-    To be used in reply message handlers.
-    :param field_type: type of the field to be initialized (as defined in vpe.api)
-    :param c_name: name of the message struct member that stores initialization value
-    :param field_reference_name: name of the field reference in generated code
-    :param field_name: name of the field (camelcase)
-    :param field_length: integer or name of variable that stores field length
-    :param object_name: name of the object to be initialized
+    Generates msg handler registration for all messages except for dumps and requests.
+    :param messages: collection of VPP API messages.
     """
-
-    # todo move validation to vppapigen
-    if field_type.endswith('[]') and field_length == '0':
-        raise Exception('Variable array \'%s\' defined in \'%s\' '
-                        'should have defined length (e.g. \'%s[%s_length]\''
-                        % (c_name, handler_name, c_name, c_name))
-
-    if is_variable_len_array:
-        length_var_name = field_length
-        field_length = variable_length_array_value_template.substitute(length_var_name=length_var_name)
-        if length_field_type != 'u8':  # we need net to host conversion:
-            field_length = variable_length_array_template.substitute(
-                    length_field_type=length_field_type, value=field_length)
-
-    # for retval don't generate setters
-    if util.is_retval_field(c_name):
-        return ""
-
-    jni_signature = util.jni_2_signature_mapping[field_type]
-    jni_setter = util.jni_field_accessors[field_type]
-
-    result = dto_field_id_template.substitute(
-            field_reference_name=field_reference_name,
-            field_name=field_name,
-            class_ref_name=ref_name,
-            jni_signature=jni_signature)
-
-    dto_setter_template = dto_field_setter_templates[field_type]
-
-    result += dto_setter_template.substitute(
-            jni_signature=jni_signature,
-            object_name=object_name,
-            field_reference_name=field_reference_name,
-            c_name=c_name,
-            jni_setter=jni_setter,
-            field_length=field_length)
-    return result
-
-
-request_field_identifier_template = Template("""
-    jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${object_name}Class, "${field_name}", "${jni_signature}");
-    ${jni_type} ${field_reference_name} = (*env)->Get${jni_getter}(env, ${object_name}, ${field_reference_name}FieldId);
-    """)
-
-array_length_enforcement_template = Template("""
-        size_t max_size = ${field_length};
-        if (cnt > max_size) cnt = max_size;""")
-
-u8_struct_setter_template = Template("""
-    mp->${c_name} = ${field_reference_name};""")
-
-u16_struct_setter_template = Template("""
-    mp->${c_name} = clib_host_to_net_u16(${field_reference_name});""")
-
-u32_struct_setter_template = Template("""
-    mp->${c_name} = clib_host_to_net_u32(${field_reference_name});""")
-
-i32_struct_setter_template = Template("""
-    mp->${c_name} = clib_host_to_net_i32(${field_reference_name});!""")
-
-u64_struct_setter_template = Template("""
-    mp->${c_name} = clib_host_to_net_u64(${field_reference_name});""")
-
-u8_array_struct_setter_template = Template("""
-    if (${field_reference_name}) {
-        jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
-        ${field_length_check}
-        (*env)->GetByteArrayRegion(env, ${field_reference_name}, 0, cnt, (jbyte *)mp->${c_name});
-    }
+    handlers = []
+    for msg in messages:
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control_ping managed by jvpp registry.
+            continue
+        if is_dump(msg) or is_request(msg):
+            continue
+        name = msg.name
+        crc = msg.crc
+        handlers.append("_(%s_%s, %s) \\" % (name, crc, name))
+    return _HANDLER_REGISTRATION_TEMPLATE.substitute(handlers="\n".join(handlers))
+
+_HANDLER_REGISTRATION_TEMPLATE = Template("""
+// Registration of message handlers in vlib
+#define foreach_api_reply_handler \\
+$handlers
 """)
-
-u16_array_struct_setter_template = Template("""
-    if (${field_reference_name}) {
-        jshort * ${field_reference_name}ArrayElements = (*env)->GetShortArrayElements(env, ${field_reference_name}, NULL);
-        size_t _i;
-        jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
-        ${field_length_check}
-        for (_i = 0; _i < cnt; _i++) {
-            mp->${c_name}[_i] = clib_host_to_net_u16(${field_reference_name}ArrayElements[_i]);
-        }
-        (*env)->ReleaseShortArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
-    }
-    """)
-
-u32_array_struct_setter_template = Template("""
-    if (${field_reference_name}) {
-        jint * ${field_reference_name}ArrayElements = (*env)->GetIntArrayElements(env, ${field_reference_name}, NULL);
-        size_t _i;
-        jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
-        ${field_length_check}
-        for (_i = 0; _i < cnt; _i++) {
-            mp->${c_name}[_i] = clib_host_to_net_u32(${field_reference_name}ArrayElements[_i]);
-        }
-        (*env)->ReleaseIntArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
-    }
-    """)
-
-u64_array_struct_setter_template = Template("""
-    if (${field_reference_name}) {
-        jlong * ${field_reference_name}ArrayElements = (*env)->GetLongArrayElements(env, ${field_reference_name}, NULL);
-        size_t _i;
-        jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
-        ${field_length_check}
-        for (_i = 0; _i < cnt; _i++) {
-            mp->${c_name}[_i] = clib_host_to_net_u64(${field_reference_name}ArrayElements[_i]);
-        }
-        (*env)->ReleaseLongArrayElements (env, ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
-    }
-    """)
-
-struct_setter_templates = {'u8': u8_struct_setter_template,
-                           'u16': u16_struct_setter_template,
-                           'u32': u32_struct_setter_template,
-                           'i32': u32_struct_setter_template,
-                           'u64': u64_struct_setter_template,
-                           'u8[]': u8_array_struct_setter_template,
-                           'u16[]': u16_array_struct_setter_template,
-                           'u32[]': u32_array_struct_setter_template,
-                           'u64[]': u64_array_struct_setter_template
-                           }
-
-
-def jni_request_identifiers_for_type(field_type, field_reference_name, field_name, object_name="request"):
-    """
-    Generates jni code that defines C variable corresponding to field of java object
-    (dto or custom type). To be used in request message handlers.
-    :param field_type: type of the field to be initialized (as defined in vpe.api)
-    :param field_reference_name: name of the field reference in generated code
-    :param field_name: name of the field (camelcase)
-    :param object_name: name of the object to be initialized
-    """
-    # field identifiers
-    jni_type = util.vpp_2_jni_type_mapping[field_type]
-    jni_signature = util.jni_2_signature_mapping[field_type]
-    jni_getter = util.jni_field_accessors[field_type]
-
-    # field identifier
-    return request_field_identifier_template.substitute(
-            jni_type=jni_type,
-            field_reference_name=field_reference_name,
-            field_name=field_name,
-            jni_signature=jni_signature,
-            jni_getter=jni_getter,
-            object_name=object_name)
-
-
-def jni_request_binding_for_type(field_type, c_name, field_reference_name, field_length, is_variable_len_array):
-    """
-    Generates jni code that initializes C structure that corresponds to a field of java object
-    (dto or custom type). To be used in request message handlers.
-    :param field_type: type of the field to be initialized (as defined in vpe.api)
-    :param c_name: name of the message struct member to be initialized
-    :param field_reference_name: name of the field reference in generated code
-    :param field_length: integer or name of variable that stores field length
-    """
-
-    # field setter
-    field_length_check = ""
-
-    # check if we are processing variable length array:
-    if is_variable_len_array:
-        field_length = util.underscore_to_camelcase(field_length)
-
-    # enforce max length if array has fixed length or uses variable length syntax
-    if str(field_length) != "0":
-        field_length_check = array_length_enforcement_template.substitute(field_length=field_length)
-
-    struct_setter_template = struct_setter_templates[field_type]
-
-    msg_initialization = struct_setter_template.substitute(
-            c_name=c_name,
-            field_reference_name=field_reference_name,
-            field_length_check=field_length_check)
-
-    return msg_initialization
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jni_impl_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jni_impl_gen.py
new file mode 100755 (executable)
index 0000000..bf75236
--- /dev/null
@@ -0,0 +1,115 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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 jni_common_gen import generate_j2c_identifiers, generate_j2c_swap
+from jvpp_model import is_dump, is_request, is_control_ping, is_control_ping_reply
+
+
+def generate_jni_impl(model):
+    """
+    Generates JNI bindings for sending dump and request messages.
+    :param model: meta-model of VPP API used for jVPP generation.
+    """
+    jni_impl = []
+    for msg in model.messages:
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control ping managed by jvpp registry.
+            continue
+        if not (is_dump(msg) or is_request(msg)):
+            continue
+        arguments = ""
+        request_class = ""
+        jni_identifiers = ""
+        msg_initialization = ""
+
+        if msg.has_fields:
+            arguments = ", jobject request"
+            request_class = _REQUEST_CLASS_TEMPLATE.substitute(
+                plugin_name=model.plugin_name,
+                java_dto_name=msg.java_name_upper
+            )
+            jni_identifiers = generate_j2c_identifiers(msg, class_ref_name="requestClass", object_ref_name="request")
+            msg_initialization = generate_j2c_swap(msg, struct_ref_name="mp")
+
+        jni_impl.append(_JNI_IMPL_TEMPLATE.substitute(
+            c_name=msg.name,
+            json_filename=model.json_api_files,
+            json_definition=msg.doc,
+            plugin_name=model.plugin_name,
+            plugin_java_name=model.plugin_java_name,
+            java_method_name=msg.java_name_lower,
+            arguments=arguments,
+            request_class=request_class,
+            jni_identifiers=jni_identifiers,
+            msg_size=_generate_msg_size(msg),
+            crc=msg.crc,
+            msg_initialization=msg_initialization
+        ))
+    return "".join(jni_impl)
+
+
+_JNI_IMPL_TEMPLATE = Template("""
+/**
+ * JNI binding for sending ${c_name} message.
+ * Generated based on $json_filename:
+$json_definition
+ */
+JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_${plugin_name}_JVpp${plugin_java_name}Impl_${java_method_name}0
+(JNIEnv * env, jclass clazz${arguments}) {
+    ${plugin_name}_main_t *plugin_main = &${plugin_name}_main;
+    vl_api_${c_name}_t * mp;
+    u32 my_context_id = vppjni_get_context_id (&jvpp_main);
+$request_class
+$jni_identifiers
+
+    // create message:
+    const size_t _size = ${msg_size};
+    mp = vl_msg_api_alloc(_size);
+    memset (mp, 0, _size);
+    mp->_vl_msg_id = ntohs (get_message_id(env, "${c_name}_${crc}"));
+    mp->client_index = plugin_main->my_client_index;
+    mp->context = clib_host_to_net_u32 (my_context_id);
+
+$msg_initialization
+
+    // send message:
+    if (CLIB_DEBUG > 1)
+        clib_warning ("Sending ${c_name} message");
+    vl_msg_api_send_shmem (plugin_main->vl_input_queue, (u8 *)&mp);
+    if ((*env)->ExceptionCheck(env)) {
+        return JNI_ERR;
+    }
+    return my_context_id;
+}""")
+
+# TODO: cache method and field identifiers to achieve better performance
+# https://jira.fd.io/browse/HONEYCOMB-42
+_REQUEST_CLASS_TEMPLATE = Template("""    jclass requestClass = (*env)->FindClass(env, "io/fd/vpp/jvpp/${plugin_name}/dto/${java_dto_name}");
+""")
+
+
+def _generate_msg_size(msg):
+    msg_size = "sizeof(*mp)"
+    _size_components = []
+    for field in msg.fields:
+        # Ignore ZLAs for simplicity (to support them we need to call JNI functions to check actual size)
+        if field.array_len_field:
+            _size_components += " + %s*sizeof(%s)" % (field.array_len_field.java_name, field.type.base_type.vpp_name)
+            # FIXME(VPP-586): for proper nested structures support, we need generate functions computing type sizes
+            # and use it instead of sizeof
+
+    return msg_size + "".join(_size_components)
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jni_msg_handlers_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jni_msg_handlers_gen.py
new file mode 100755 (executable)
index 0000000..8f6410f
--- /dev/null
@@ -0,0 +1,105 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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 jni_common_gen import generate_c2j_swap
+from jvpp_model import is_dump, is_request, is_control_ping, is_control_ping_reply, is_retval
+
+
+def generate_jni_handlers(model):
+    """
+    Generates msg handlers for all messages except for dumps and requests (handled by vpp, not client).
+    :param model: meta-model of VPP API used for jVPP generation.
+    """
+    jni_impl = []
+    for msg in model.messages:
+        msg_name = msg.name
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control ping managed by jvpp registry.
+            continue
+        if is_dump(msg) or is_request(msg):
+            continue
+
+        jni_impl.append(_MSG_HANDLER_TEMPLATE.substitute(
+            c_name=msg_name,
+            json_filename=model.json_api_files,
+            json_definition=msg.doc,
+            plugin_name=model.plugin_name,
+            err_handler=_generate_error_handler(msg),
+            class_ref_name=msg.java_name_lower,
+            dto_name=msg.java_name_upper,
+            dto_setters=generate_c2j_swap(msg, object_ref_name="dto", struct_ref_name="mp")
+        ))
+    return "".join(jni_impl)
+
+_MSG_HANDLER_TEMPLATE = Template("""
+/**
+ * Handler for ${c_name} message.
+ * Generated based on $json_filename:
+$json_definition
+ */
+static void vl_api_${c_name}_t_handler (vl_api_${c_name}_t * mp)
+{
+    ${plugin_name}_main_t *plugin_main = &${plugin_name}_main;
+    JNIEnv *env = jvpp_main.jenv;
+    jthrowable exc;
+$err_handler
+
+    if (CLIB_DEBUG > 1)
+        clib_warning ("Received ${c_name} event message");
+
+    jmethodID constructor = (*env)->GetMethodID(env, ${class_ref_name}Class, "<init>", "()V");
+
+    // User does not have to provide callbacks for all VPP messages.
+    // We are ignoring messages that are not supported by user.
+    (*env)->ExceptionClear(env); // just in case exception occurred in different place and was not properly cleared
+    jmethodID callbackMethod = (*env)->GetMethodID(env, plugin_main->callbackClass, "on${dto_name}", "(Lio/fd/vpp/jvpp/${plugin_name}/dto/${dto_name};)V");
+    exc = (*env)->ExceptionOccurred(env);
+    if (exc) {
+        clib_warning("Unable to extract on${dto_name} method reference from ${plugin_name} plugin's callbackClass. Ignoring message.\\n");
+        (*env)->ExceptionDescribe(env);
+        (*env)->ExceptionClear(env);
+        return;
+    }
+
+    jobject dto = (*env)->NewObject(env, ${class_ref_name}Class, constructor);
+$dto_setters
+
+    (*env)->CallVoidMethod(env, plugin_main->callbackObject, callbackMethod, dto);
+    // free DTO as per http://stackoverflow.com/questions/1340938/memory-leak-when-calling-java-code-from-c-using-jni
+    (*env)->DeleteLocalRef(env, dto);
+}""")
+
+
+def _generate_error_handler(msg):
+    err_handler = ""
+    for field in msg.fields:
+        if is_retval(field):
+            err_handler = _ERR_HANDLER_TEMPLATE.substitute(name=msg.name)
+    return err_handler
+
+# Code fragment for checking result of the operation before sending request reply.
+# Error checking is optional (some messages, e.g. detail messages do not have retval field).
+_ERR_HANDLER_TEMPLATE = Template("""
+    // for negative result don't send callback message but send error callback
+    if (mp->retval<0) {
+        call_on_error("${name}", mp->context, mp->retval, plugin_main->callbackClass, plugin_main->callbackObject, callbackExceptionClass);
+        return;
+    }
+    if (mp->retval == VNET_API_ERROR_IN_PROGRESS) {
+        clib_warning("Result in progress");
+        return;
+    }""")
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jni_type_handlers_gen.py
new file mode 100755 (executable)
index 0000000..eb6ab23
--- /dev/null
@@ -0,0 +1,78 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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 jni_common_gen import generate_j2c_swap, generate_j2c_identifiers, generate_c2j_swap
+from jvpp_model import Class
+
+
+def generate_type_handlers(model):
+    """
+    Generates msg handlers for all messages except for dumps and requests (handled by vpp, not client).
+    :param model: meta-model of VPP API used for jVPP generation.
+    """
+    type_handlers = []
+    for t in model.types:
+        if not isinstance(t, Class):
+            continue
+        ref_name = t.java_name_lower
+        jni_identifiers = generate_j2c_identifiers(t, class_ref_name="%sClass" % ref_name, object_ref_name="_host")
+        type_handlers.append(_TYPE_NET_TO_HOST_TEMPLATE.substitute(
+            c_name=t.name,
+            json_filename=model.json_api_files,
+            json_definition=t.doc,
+            type_reference_name=ref_name,
+            class_FQN=t.jni_name,
+            jni_identifiers=jni_identifiers,
+            type_swap=generate_j2c_swap(t, struct_ref_name="_net")
+        ))
+
+        type_handlers.append(_TYPE_HOST_TO_NET_TEMPLATE.substitute(
+            c_name=t.name,
+            json_filename=model.json_api_files,
+            json_definition=t.doc,
+            type_reference_name=ref_name,
+            class_FQN=t.jni_name,
+            jni_identifiers=jni_identifiers,
+            type_swap=generate_c2j_swap(t, object_ref_name="_host", struct_ref_name="_net")
+        ))
+
+    return "\n".join(type_handlers)
+
+_TYPE_NET_TO_HOST_TEMPLATE = Template("""
+/**
+ * Host to network byte order conversion for ${c_name} type.
+ * 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 ${type_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
+$jni_identifiers
+$type_swap
+}""")
+
+_TYPE_HOST_TO_NET_TEMPLATE = Template("""
+/**
+ * Network to host byte order conversion for ${c_name} type.
+ * Generated based on $json_filename:
+$json_definition
+ */
+static inline void _net_to_host_${c_name}(JNIEnv * env, vl_api_${c_name}_t * _net, jobject _host)
+{
+    jclass ${type_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
+$type_swap
+}""")
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py
deleted file mode 100644 (file)
index b97a9f9..0000000
+++ /dev/null
@@ -1,389 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (c) 2016 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.
-#
-
-import util
-from string import Template
-
-import jni_gen
-
-
-def is_manually_generated(f_name):
-    return f_name in {'control_ping_reply'}
-
-
-class_reference_template = Template("""jclass ${ref_name}Class;
-""")
-
-find_class_invocation_template = Template("""
-    ${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "io/fd/vpp/jvpp/${plugin_name}/dto/${class_name}"));
-    if ((*env)->ExceptionCheck(env)) {
-        (*env)->ExceptionDescribe(env);
-        return JNI_ERR;
-    }""")
-
-find_class_template = Template("""
-    ${ref_name}Class = (jclass)(*env)->NewGlobalRef(env, (*env)->FindClass(env, "${class_name}"));
-    if ((*env)->ExceptionCheck(env)) {
-        (*env)->ExceptionDescribe(env);
-        return JNI_ERR;
-    }""")
-
-delete_class_invocation_template = Template("""
-    if (${ref_name}Class) {
-        (*env)->DeleteGlobalRef(env, ${ref_name}Class);
-    }""")
-
-class_cache_template = Template("""
-$class_references
-static int cache_class_references(JNIEnv* env) {
-    $find_class_invocations
-    return 0;
-}
-
-static void delete_class_references(JNIEnv* env) {
-    $delete_class_invocations
-}""")
-
-
-def generate_class_cache(func_list, plugin_name):
-    class_references = []
-    find_class_invocations = []
-    delete_class_invocations = []
-    for f in func_list:
-        c_name = f['name']
-        class_name = util.underscore_to_camelcase_upper(c_name)
-        ref_name = util.underscore_to_camelcase(c_name)
-
-        if util.is_control_ping(class_name):
-            continue
-
-        class_references.append(class_reference_template.substitute(
-            ref_name=ref_name))
-        find_class_invocations.append(find_class_invocation_template.substitute(
-            plugin_name=plugin_name,
-            ref_name=ref_name,
-            class_name=class_name))
-        delete_class_invocations.append(delete_class_invocation_template.substitute(ref_name=ref_name))
-
-    # add exception class to class cache
-    ref_name = 'callbackException'
-    class_name = 'io/fd/vpp/jvpp/VppCallbackException'
-    class_references.append(class_reference_template.substitute(
-            ref_name=ref_name))
-    find_class_invocations.append(find_class_template.substitute(
-            ref_name=ref_name,
-            class_name=class_name))
-    delete_class_invocations.append(delete_class_invocation_template.substitute(ref_name=ref_name))
-
-    return class_cache_template.substitute(
-        class_references="".join(class_references), find_class_invocations="".join(find_class_invocations),
-        delete_class_invocations="".join(delete_class_invocations))
-
-
-# TODO: cache method and field identifiers to achieve better performance
-# https://jira.fd.io/browse/HONEYCOMB-42
-request_class_template = Template("""
-    jclass requestClass = (*env)->FindClass(env, "io/fd/vpp/jvpp/${plugin_name}/dto/${java_name_upper}");""")
-
-request_field_identifier_template = Template("""
-    jfieldID ${field_reference_name}FieldId = (*env)->GetFieldID(env, ${object_name}Class, "${field_name}", "${jni_signature}");
-    ${jni_type} ${field_reference_name} = (*env)->Get${jni_getter}(env, ${object_name}, ${field_reference_name}FieldId);
-    """)
-
-jni_msg_size_template = Template(""" + ${array_length}*sizeof(${element_type})""")
-
-jni_impl_template = Template("""
-/**
- * JNI binding for sending ${c_name} message.
- * Generated based on $inputfile preparsed data:
-$api_data
- */
-JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_${plugin_name}_JVpp${java_plugin_name}Impl_${field_name}0
-(JNIEnv * env, jclass clazz$args) {
-    ${plugin_name}_main_t *plugin_main = &${plugin_name}_main;
-    vl_api_${c_name}_t * mp;
-    u32 my_context_id = vppjni_get_context_id (&jvpp_main);
-    $request_class
-
-    $jni_identifiers
-
-    // create message:
-    mp = vl_msg_api_alloc(${msg_size});
-    memset (mp, 0, ${msg_size});
-    mp->_vl_msg_id = ntohs (get_message_id(env, "${c_name}_${crc}"));
-    mp->client_index = plugin_main->my_client_index;
-    mp->context = clib_host_to_net_u32 (my_context_id);
-
-    $msg_initialization
-
-    // send message:
-    if (CLIB_DEBUG > 1)
-        clib_warning ("Sending ${field_name} event message");
-    vl_msg_api_send_shmem (plugin_main->vl_input_queue, (u8 *)&mp);
-    if ((*env)->ExceptionCheck(env)) {
-        return JNI_ERR;
-    }
-    return my_context_id;
-}""")
-
-
-def generate_jni_impl(func_list, plugin_name, inputfile):
-    jni_impl = []
-    for f in func_list:
-        f_name = f['name']
-        camel_case_function_name = util.underscore_to_camelcase(f_name)
-        if is_manually_generated(f_name):
-            # Skip control ping managed by jvpp registry.
-            continue
-        if not (util.is_dump(f_name) or util.is_request(f_name, func_list)):
-            continue
-
-        # Generate jni bindings for sending dump and request messages.
-        arguments = ''
-        request_class = ''
-        jni_identifiers = ''
-        msg_initialization = ''
-        f_name_uppercase = f_name.upper()
-        msg_size = 'sizeof(*mp)'
-
-        if f['args']:
-            arguments = ', jobject request'
-            camel_case_function_name_upper = util.underscore_to_camelcase_upper(f_name)
-
-            request_class = request_class_template.substitute(
-                    java_name_upper=camel_case_function_name_upper,
-                    plugin_name=plugin_name)
-
-            for t in zip(f['types'], f['args'], f['lengths'], f['arg_types']):
-                field_name = util.underscore_to_camelcase(t[1])
-                is_variable_len_array = t[2][1]
-                if is_variable_len_array:
-                    msg_size += jni_msg_size_template.substitute(array_length=util.underscore_to_camelcase(t[2][0]),
-                                                                 element_type=t[3])
-                jni_identifiers += jni_gen.jni_request_identifiers_for_type(field_type=t[0],
-                                                                            field_reference_name=field_name,
-                                                                            field_name=field_name)
-                msg_initialization += jni_gen.jni_request_binding_for_type(field_type=t[0], c_name=t[1],
-                                                                           field_reference_name=field_name,
-                                                                           field_length=t[2][0],
-                                                                           is_variable_len_array=is_variable_len_array)
-
-        jni_impl.append(jni_impl_template.substitute(
-                inputfile=inputfile,
-                api_data=util.api_message_to_javadoc(f),
-                field_reference_name=camel_case_function_name,
-                field_name=camel_case_function_name,
-                c_name_uppercase=f_name_uppercase,
-                c_name=f_name,
-                crc=f['crc'],
-                plugin_name=plugin_name,
-                java_plugin_name=plugin_name.title(),
-                request_class=request_class,
-                jni_identifiers=jni_identifiers,
-                msg_size=msg_size,
-                msg_initialization=msg_initialization,
-                args=arguments))
-
-    return "\n".join(jni_impl)
-
-# code fragment for checking result of the operation before sending request reply
-callback_err_handler_template = Template("""
-    // for negative result don't send callback message but send error callback
-    if (mp->retval<0) {
-        call_on_error("${handler_name}", mp->context, mp->retval, plugin_main->callbackClass, plugin_main->callbackObject, callbackExceptionClass);
-        return;
-    }
-    if (mp->retval == VNET_API_ERROR_IN_PROGRESS) {
-        clib_warning("Result in progress");
-        return;
-    }
-""")
-
-msg_handler_template = Template("""
-/**
- * Handler for ${handler_name} message.
- * Generated based on $inputfile preparsed data:
-$api_data
- */
-static void vl_api_${handler_name}_t_handler (vl_api_${handler_name}_t * mp)
-{
-    ${plugin_name}_main_t *plugin_main = &${plugin_name}_main;
-    JNIEnv *env = jvpp_main.jenv;
-    jthrowable exc;
-    $err_handler
-
-    if (CLIB_DEBUG > 1)
-        clib_warning ("Received ${handler_name} event message");
-
-    jmethodID constructor = (*env)->GetMethodID(env, ${class_ref_name}Class, "<init>", "()V");
-
-    // User does not have to provide callbacks for all VPP messages.
-    // We are ignoring messages that are not supported by user.
-    (*env)->ExceptionClear(env); // just in case exception occurred in different place and was not properly cleared
-    jmethodID callbackMethod = (*env)->GetMethodID(env, plugin_main->callbackClass, "on${dto_name}", "(Lio/fd/vpp/jvpp/${plugin_name}/dto/${dto_name};)V");
-    exc = (*env)->ExceptionOccurred(env);
-    if (exc) {
-        clib_warning("Unable to extract on${dto_name} method reference from ${plugin_name} plugin's callbackClass. Ignoring message.\\n");
-        (*env)->ExceptionDescribe(env);
-        (*env)->ExceptionClear(env);
-        return;
-    }
-
-    jobject dto = (*env)->NewObject(env, ${class_ref_name}Class, constructor);
-    $dto_setters
-
-    (*env)->CallVoidMethod(env, plugin_main->callbackObject, callbackMethod, dto);
-    // free DTO as per http://stackoverflow.com/questions/1340938/memory-leak-when-calling-java-code-from-c-using-jni
-    (*env)->DeleteLocalRef(env, dto);
-}""")
-
-
-def generate_msg_handlers(func_list, plugin_name, inputfile):
-    handlers = []
-    for f in func_list:
-        handler_name = f['name']
-        dto_name = util.underscore_to_camelcase_upper(handler_name)
-        ref_name = util.underscore_to_camelcase(handler_name)
-
-        if is_manually_generated(handler_name):
-            # Skip control ping managed by jvpp registry.
-            continue
-        if util.is_dump(handler_name) or util.is_request(handler_name, func_list):
-            continue
-
-        # Generate msg handlers for all messages except for dumps and requests (handled by vpp, not client).
-        dto_setters = ''
-        err_handler = ''
-        # dto setters
-        for t in zip(f['types'], f['args'], f['lengths']):
-            c_name = t[1]
-            java_name = util.underscore_to_camelcase(c_name)
-            field_length = t[2][0]
-            is_variable_len_array = t[2][1]
-            length_field_type = None
-            if is_variable_len_array:
-                length_field_type = f['types'][f['args'].index(field_length)]
-            dto_setters += jni_gen.jni_reply_handler_for_type(handler_name=handler_name, ref_name=ref_name,
-                                                              field_type=t[0], c_name=t[1],
-                                                              field_reference_name=java_name,
-                                                              field_name=java_name, field_length=field_length,
-                                                              is_variable_len_array=is_variable_len_array,
-                                                              length_field_type=length_field_type)
-
-            # for retval don't generate setters and generate retval check
-            if util.is_retval_field(c_name):
-                err_handler = callback_err_handler_template.substitute(
-                    handler_name=handler_name
-                )
-                continue
-
-        handlers.append(msg_handler_template.substitute(
-            inputfile=inputfile,
-            api_data=util.api_message_to_javadoc(f),
-            handler_name=handler_name,
-            plugin_name=plugin_name,
-            dto_name=dto_name,
-            class_ref_name=ref_name,
-            dto_setters=dto_setters,
-            err_handler=err_handler))
-
-    return "\n".join(handlers)
-
-
-handler_registration_template = Template("""_(${name}_${crc}, ${name}) \\
-""")
-
-
-def generate_handler_registration(func_list):
-    handler_registration = ["#define foreach_api_reply_handler \\\n"]
-    for f in func_list:
-        name = f['name']
-        camelcase_name = util.underscore_to_camelcase(name)
-
-        if util.is_control_ping(camelcase_name):
-            # Skip control ping managed by registry.
-            continue
-        if util.is_dump(name) or util.is_request(name, func_list):
-            continue
-
-        # Generate msg handler registration for all messages except for dumps and requests.
-        handler_registration.append(handler_registration_template.substitute(
-            name=name,
-            crc=f['crc']))
-
-    return "".join(handler_registration)
-
-
-api_verification_template = Template("""_(${name}_${crc}) \\
-""")
-
-
-def generate_api_verification(func_list):
-    api_verification = ["#define foreach_supported_api_message \\\n"]
-    for f in func_list:
-        name = f['name']
-
-        api_verification.append(api_verification_template.substitute(
-            name=name,
-            crc=f['crc']))
-
-    return "".join(api_verification)
-
-
-jvpp_c_template = Template("""/**
- * This file contains JNI bindings for jvpp Java API.
- * It was generated by jvpp_c_gen.py based on $inputfile
- * (python representation of api file generated by vppapigen).
- */
-
-// JAVA class reference cache
-$class_cache
-
-// List of supported API messages used for verification
-$api_verification
-
-// JNI bindings
-$jni_implementations
-
-// Message handlers
-$msg_handlers
-
-// Registration of message handlers in vlib
-$handler_registration
-""")
-
-
-def generate_jvpp(func_list, plugin_name, inputfile, path, logger):
-    """ Generates jvpp C file """
-    logger.debug("Generating jvpp C for %s" % inputfile)
-
-    class_cache = generate_class_cache(func_list, plugin_name)
-    jni_impl = generate_jni_impl(func_list, plugin_name, inputfile)
-    msg_handlers = generate_msg_handlers(func_list, plugin_name, inputfile)
-    handler_registration = generate_handler_registration(func_list)
-    api_verification = generate_api_verification(func_list)
-
-    jvpp_c_file = open("%s/jvpp_%s_gen.h" % (path, plugin_name), 'w')
-    jvpp_c_file.write(jvpp_c_template.substitute(
-            inputfile=inputfile,
-            class_cache=class_cache,
-            api_verification=api_verification,
-            jni_implementations=jni_impl,
-            msg_handlers=msg_handlers,
-            handler_registration=handler_registration))
-    jvpp_c_file.flush()
-    jvpp_c_file.close()
-
index c378721..c5634de 100644 (file)
@@ -1,6 +1,6 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2
 #
-# Copyright (c) 2016 Cisco and/or its affiliates.
+# Copyright (c) 2016,2018 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:
 # 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.
-
-import os, util
 from string import Template
 
-import callback_gen
+from jvpp_model import is_control_ping, is_dump, is_request, is_event, is_control_ping_reply
+
+
+def generate_callback_facade(work_dir, model, logger):
+    """ Generates callback facade """
+    logger.debug("Generating JVpp callback facade for %s" % model.json_api_files)
+    _generate_ifc(work_dir, model),
+    _generate_impl(work_dir, model)
+    _generate_callback(work_dir, model)
+
+
+def _generate_ifc(work_dir, model):
+    with open("%s/CallbackJVpp%s.java" % (work_dir, model.plugin_java_name), "w") as f:
+        f.write(_IFC_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            json_filename=model.json_api_files,
+            plugin_name=model.plugin_java_name,
+            methods=_generate_ifc_methods(model)
+        ))
 
-jvpp_ifc_template = Template("""
-package $plugin_package.$callback_facade_package;
+_IFC_TEMPLATE = Template("""
+package $plugin_package.callfacade;
 
 /**
  * <p>Callback Java API representation of $plugin_package plugin.
- * <br>It was generated by jvpp_callback_facade_gen.py based on $inputfile
- * <br>(python representation of api file generated by vppapigen).
+ * <br>It was generated by jvpp_callback_facade_gen.py based on $json_filename.
  */
-public interface CallbackJVpp${plugin_name} extends $base_package.$notification_package.EventRegistryProvider, java.lang.AutoCloseable {
+public interface CallbackJVpp${plugin_name} extends io.fd.vpp.jvpp.notification.EventRegistryProvider, java.lang.AutoCloseable {
 
     // TODO add send
 
@@ -34,29 +49,66 @@ $methods
 }
 """)
 
-jvpp_impl_template = Template("""
-package $plugin_package.$callback_facade_package;
+
+def _generate_ifc_methods(model):
+    plugin_package = model.plugin_package
+    methods = []
+    for msg in model.messages:
+        if is_control_ping(msg):
+            # Skip control ping managed by jvpp registry.
+            continue
+        if not (is_dump(msg) or is_request(msg)):
+            # Skip replies and messages that do not not have replies (e.g events/counters).
+            continue
+        template = _IFC_NO_ARG_METHOD_TEMPLATE
+        if msg.has_fields:
+            template = _IFC_METHOD_TEMPLATE
+        methods.append(template.substitute(
+            name=msg.java_name_lower,
+            plugin_package=plugin_package,
+            request=msg.java_name_upper,
+            reply=msg.reply_java
+        ))
+    return "\n".join(methods)
+
+_IFC_NO_ARG_METHOD_TEMPLATE = Template(
+    """    void $name($plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException;""")
+
+_IFC_METHOD_TEMPLATE = Template(
+    """    void $name($plugin_package.dto.$request request, $plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException;""")
+
+
+def _generate_impl(work_dir, model):
+    with open("%s/CallbackJVpp%sFacade.java" % (work_dir, model.plugin_java_name), "w") as f:
+        f.write(_IMPL_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            json_filename=model.json_api_files,
+            plugin_name=model.plugin_java_name,
+            methods=_generate_impl_methods(model)
+        ))
+
+_IMPL_TEMPLATE = Template("""
+package $plugin_package.callfacade;
 
 /**
  * <p>Default implementation of Callback${plugin_name}JVpp interface.
- * <br>It was generated by jvpp_callback_facade_gen.py based on $inputfile
- * <br>(python representation of api file generated by vppapigen).
+ * <br>It was generated by jvpp_callback_facade_gen.py based on $json_filename.
  */
 public final class CallbackJVpp${plugin_name}Facade implements CallbackJVpp${plugin_name} {
 
     private final $plugin_package.JVpp${plugin_name} jvpp;
-    private final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> callbacks;
-    private final $plugin_package.$notification_package.${plugin_name}EventRegistryImpl eventRegistry = new $plugin_package.$notification_package.${plugin_name}EventRegistryImpl();
+    private final java.util.Map<Integer, io.fd.vpp.jvpp.callback.JVppCallback> callbacks;
+    private final $plugin_package.notification.${plugin_name}EventRegistryImpl eventRegistry = new $plugin_package.notification.${plugin_name}EventRegistryImpl();
     /**
      * <p>Create CallbackJVpp${plugin_name}Facade object for provided JVpp instance.
      * Constructor internally creates CallbackJVppFacadeCallback class for processing callbacks
      * and then connects to provided JVpp instance
      *
-     * @param jvpp provided $base_package.JVpp instance
+     * @param jvpp provided io.fd.vpp.jvpp.JVpp instance
      *
      * @throws java.io.IOException in case instance cannot connect to JVPP
      */
-    public CallbackJVpp${plugin_name}Facade(final $base_package.JVppRegistry registry, final $plugin_package.JVpp${plugin_name} jvpp) throws java.io.IOException {
+    public CallbackJVpp${plugin_name}Facade(final io.fd.vpp.jvpp.JVppRegistry registry, final $plugin_package.JVpp${plugin_name} jvpp) throws java.io.IOException {
         this.jvpp = java.util.Objects.requireNonNull(jvpp,"jvpp is null");
         this.callbacks = new java.util.HashMap<>();
         java.util.Objects.requireNonNull(registry, "JVppRegistry should not be null");
@@ -64,7 +116,7 @@ public final class CallbackJVpp${plugin_name}Facade implements CallbackJVpp${plu
     }
 
     @Override
-    public $plugin_package.$notification_package.${plugin_name}EventRegistry getEventRegistry() {
+    public $plugin_package.notification.${plugin_name}EventRegistry getEventRegistry() {
         return eventRegistry;
     }
 
@@ -79,139 +131,76 @@ $methods
 }
 """)
 
-method_template = Template(
-    """    void $name($plugin_package.$dto_package.$request request, $plugin_package.$callback_package.$callback callback) throws $base_package.VppInvocationException;""")
 
-method_impl_template = Template("""    public final void $name($plugin_package.$dto_package.$request request, $plugin_package.$callback_package.$callback callback) throws $base_package.VppInvocationException {
+def _generate_impl_methods(model):
+    plugin_package = model.plugin_package
+    methods = []
+    for msg in model.messages:
+        if is_control_ping(msg):
+            # Skip control ping managed by jvpp registry.
+            continue
+        if not (is_dump(msg) or is_request(msg)):
+            # Skip replies and messages that do not not have replies (e.g events/counters).
+            continue
+        template = _IMPL_NO_ARG_METHOD_TEMPLATE
+        if msg.has_fields:
+            template = _IMPL_METHOD_TEMPLATE
+        methods.append(template.substitute(
+            name=msg.java_name_lower,
+            plugin_package=plugin_package,
+            request=msg.java_name_upper,
+            reply=msg.reply_java
+        ))
+    return "\n".join(methods)
+
+_IMPL_NO_ARG_METHOD_TEMPLATE = Template(
+    """    public final void $name($plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException {
         synchronized (callbacks) {
-            callbacks.put(jvpp.$name(request), callback);
+            callbacks.put(jvpp.$name(), callback);
         }
     }
 """)
 
-no_arg_method_template = Template("""    void $name($plugin_package.$callback_package.$callback callback) throws $base_package.VppInvocationException;""")
-no_arg_method_impl_template = Template("""    public final void $name($plugin_package.$callback_package.$callback callback) throws $base_package.VppInvocationException {
+_IMPL_METHOD_TEMPLATE = Template("""    public final void $name($plugin_package.dto.$request request, $plugin_package.callback.${reply}Callback callback) throws io.fd.vpp.jvpp.VppInvocationException {
         synchronized (callbacks) {
-            callbacks.put(jvpp.$name(), callback);
+            callbacks.put(jvpp.$name(request), callback);
         }
     }
 """)
 
 
-def generate_jvpp(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package,
-                  notification_package, callback_facade_package, inputfile, logger):
-    """ Generates callback facade """
-    logger.debug("Generating JVpp callback facade for %s" % inputfile)
-
-    if os.path.exists(callback_facade_package):
-        util.remove_folder(callback_facade_package)
-
-    os.mkdir(callback_facade_package)
-
-    methods = []
-    methods_impl = []
-
-    # Generate methods for sending messages.
-    for func in func_list:
-        camel_case_name = util.underscore_to_camelcase(func['name'])
-        camel_case_name_upper = util.underscore_to_camelcase_upper(func['name'])
-        if util.is_reply(camel_case_name) or util.is_control_ping(camel_case_name):
-            continue
+def _generate_callback(work_dir, model):
+    with open("%s/CallbackJVpp%sFacadeCallback.java" % (work_dir, model.plugin_java_name), "w") as f:
+        f.write(_CALLBACK_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            json_filename=model.json_api_files,
+            plugin_name=model.plugin_java_name,
+            methods=_generate_callback_methods(model)
+        ))
 
-        # Strip suffix for dump calls
-        callback_type = get_request_name(camel_case_name_upper)
-        if util.is_dump(camel_case_name_upper):
-            callback_type += "Details"
-        elif util.is_request(func['name'], func_list):
-            callback_type += "Reply"
-        else:
-            # Skip messages that do not not have replies (e.g events/counters).
-            continue
-        callback_type += callback_gen.callback_suffix
-
-        if len(func['args']) == 0:
-            methods.append(no_arg_method_template.substitute(name=camel_case_name,
-                                                             base_package=base_package,
-                                                             plugin_package=plugin_package,
-                                                             dto_package=dto_package,
-                                                             callback_package=callback_package,
-                                                             callback=callback_type))
-            methods_impl.append(no_arg_method_impl_template.substitute(name=camel_case_name,
-                                                                       base_package=base_package,
-                                                                       plugin_package=plugin_package,
-                                                                       dto_package=dto_package,
-                                                                       callback_package=callback_package,
-                                                                       callback=callback_type))
-        else:
-            methods.append(method_template.substitute(name=camel_case_name,
-                                                      request=camel_case_name_upper,
-                                                      base_package=base_package,
-                                                      plugin_package=plugin_package,
-                                                      dto_package=dto_package,
-                                                      callback_package=callback_package,
-                                                      callback=callback_type))
-            methods_impl.append(method_impl_template.substitute(name=camel_case_name,
-                                                                request=camel_case_name_upper,
-                                                                base_package=base_package,
-                                                                plugin_package=plugin_package,
-                                                                dto_package=dto_package,
-                                                                callback_package=callback_package,
-                                                                callback=callback_type))
-
-    join = os.path.join(callback_facade_package, "CallbackJVpp%s.java" % plugin_name)
-    jvpp_file = open(join, 'w')
-    jvpp_file.write(
-        jvpp_ifc_template.substitute(inputfile=inputfile,
-                                     methods="\n".join(methods),
-                                     base_package=base_package,
-                                     plugin_package=plugin_package,
-                                     plugin_name=plugin_name,
-                                     dto_package=dto_package,
-                                     notification_package=notification_package,
-                                     callback_facade_package=callback_facade_package))
-    jvpp_file.flush()
-    jvpp_file.close()
-
-    jvpp_file = open(os.path.join(callback_facade_package, "CallbackJVpp%sFacade.java" % plugin_name), 'w')
-    jvpp_file.write(jvpp_impl_template.substitute(inputfile=inputfile,
-                                                  methods="\n".join(methods_impl),
-                                                  base_package=base_package,
-                                                  plugin_package=plugin_package,
-                                                  plugin_name=plugin_name,
-                                                  dto_package=dto_package,
-                                                  notification_package=notification_package,
-                                                  callback_package=callback_package,
-                                                  callback_facade_package=callback_facade_package))
-    jvpp_file.flush()
-    jvpp_file.close()
-
-    generate_callback(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, notification_package, callback_facade_package, inputfile)
-
-
-jvpp_facade_callback_template = Template("""
-package $plugin_package.$callback_facade_package;
+_CALLBACK_TEMPLATE = Template("""
+package $plugin_package.callfacade;
 
 /**
  * <p>Implementation of JVppGlobalCallback interface for Java Callback API.
- * <br>It was generated by jvpp_callback_facade_gen.py based on $inputfile
- * <br>(python representation of api file generated by vppapigen).
+ * <br>It was generated by jvpp_callback_facade_gen.py based on $json_filename.
  */
-public final class CallbackJVpp${plugin_name}FacadeCallback implements $plugin_package.$callback_package.JVpp${plugin_name}GlobalCallback {
+public final class CallbackJVpp${plugin_name}FacadeCallback implements $plugin_package.callback.JVpp${plugin_name}GlobalCallback {
 
-    private final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> requests;
-    private final $plugin_package.$notification_package.Global${plugin_name}EventCallback eventCallback;
+    private final java.util.Map<Integer, io.fd.vpp.jvpp.callback.JVppCallback> requests;
+    private final $plugin_package.notification.Global${plugin_name}EventCallback eventCallback;
     private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(CallbackJVpp${plugin_name}FacadeCallback.class.getName());
 
-    public CallbackJVpp${plugin_name}FacadeCallback(final java.util.Map<Integer, $base_package.$callback_package.JVppCallback> requestMap,
-                                      final $plugin_package.$notification_package.Global${plugin_name}EventCallback eventCallback) {
+    public CallbackJVpp${plugin_name}FacadeCallback(final java.util.Map<Integer, io.fd.vpp.jvpp.callback.JVppCallback> requestMap,
+                                      final $plugin_package.notification.Global${plugin_name}EventCallback eventCallback) {
         this.requests = requestMap;
         this.eventCallback = eventCallback;
     }
 
     @Override
-    public void onError($base_package.VppCallbackException reply) {
+    public void onError(io.fd.vpp.jvpp.VppCallbackException reply) {
 
-        $base_package.$callback_package.JVppCallback failedCall;
+        io.fd.vpp.jvpp.callback.JVppCallback failedCall;
         synchronized(requests) {
             failedCall = requests.remove(reply.getCtxId());
         }
@@ -228,12 +217,12 @@ public final class CallbackJVpp${plugin_name}FacadeCallback implements $plugin_p
 
     @Override
     @SuppressWarnings("unchecked")
-    public void onControlPingReply(final $base_package.$dto_package.ControlPingReply reply) {
+    public void onControlPingReply(final io.fd.vpp.jvpp.dto.ControlPingReply reply) {
 
-        $base_package.$callback_package.ControlPingCallback callback;
+        io.fd.vpp.jvpp.callback.ControlPingCallback callback;
         final int replyId = reply.context;
         synchronized(requests) {
-            callback = ($base_package.$callback_package.ControlPingCallback) requests.remove(replyId);
+            callback = (io.fd.vpp.jvpp.callback.ControlPingCallback) requests.remove(replyId);
         }
 
         if(callback != null) {
@@ -245,88 +234,56 @@ $methods
 }
 """)
 
-jvpp_facade_callback_method_template = Template("""
+
+def _generate_callback_methods(model):
+    plugin_package = model.plugin_package
+    methods = []
+    for msg in model.messages:
+        if is_dump(msg) or is_request(msg):
+            continue
+        if is_control_ping_reply(msg):
+            # Skip control ping managed by jvpp registry.
+            continue
+
+        # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client).
+        template = _CALLBACK_METHOD_TEMPLATE
+        if is_event(msg):
+            template = _CALLBACK_EVENT_METHOD_TEMPLATE
+        msg_name = msg.java_name_upper
+        methods.append(template.substitute(
+            message=msg_name,
+            callback="%sCallback" % msg_name,
+            plugin_package=plugin_package
+        ))
+    return "\n".join(methods)
+
+_CALLBACK_METHOD_TEMPLATE = Template("""
     @Override
     @SuppressWarnings("unchecked")
-    public void on$callback_dto(final $plugin_package.$dto_package.$callback_dto reply) {
+    public void on${message}(final $plugin_package.dto.${message} reply) {
 
-        $plugin_package.$callback_package.$callback callback;
+        $plugin_package.callback.$callback callback;
         final int replyId = reply.context;
         if (LOG.isLoggable(java.util.logging.Level.FINE)) {
-            LOG.fine(String.format("Received $callback_dto event message: %s", reply));
+            LOG.fine(String.format("Received ${message} event message: %s", reply));
         }
         synchronized(requests) {
-            callback = ($plugin_package.$callback_package.$callback) requests.remove(replyId);
+            callback = ($plugin_package.callback.$callback) requests.remove(replyId);
         }
 
         if(callback != null) {
-            callback.on$callback_dto(reply);
+            callback.on${message}(reply);
         }
     }
 """)
 
-jvpp_facade_callback_notification_method_template = Template("""
+_CALLBACK_EVENT_METHOD_TEMPLATE = Template("""
     @Override
     @SuppressWarnings("unchecked")
-    public void on$callback_dto($plugin_package.$dto_package.$callback_dto notification) {
+    public void on${message}($plugin_package.dto.${message} notification) {
         if (LOG.isLoggable(java.util.logging.Level.FINE)) {
-            LOG.fine(String.format("Received $callback_dto event message: %s", notification));
+            LOG.fine(String.format("Received ${message} event message: %s", notification));
         }
-        eventCallback.on$callback_dto(notification);
+        eventCallback.on${message}(notification);
     }
 """)
-
-
-def generate_callback(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, notification_package, callback_facade_package, inputfile):
-    callbacks = []
-    for func in func_list:
-        camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name'])
-
-        if util.is_control_ping(camel_case_name_with_suffix):
-            # Skip control ping managed by jvpp registry.
-            continue
-        if util.is_dump(func['name']) or util.is_request(func['name'], func_list):
-            continue
-
-        # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client).
-        if util.is_reply(camel_case_name_with_suffix):
-            request_method = camel_case_name_with_suffix
-            callbacks.append(jvpp_facade_callback_method_template.substitute(plugin_package=plugin_package,
-                                                                             dto_package=dto_package,
-                                                                             callback_package=callback_package,
-                                                                             callback=camel_case_name_with_suffix + callback_gen.callback_suffix,
-                                                                             callback_dto=request_method))
-        else:
-            callbacks.append(jvpp_facade_callback_notification_method_template.substitute(plugin_package=plugin_package,
-                                                                                          dto_package=dto_package,
-                                                                                          callback_package=callback_package,
-                                                                                          callback=camel_case_name_with_suffix + callback_gen.callback_suffix,
-                                                                                          callback_dto=camel_case_name_with_suffix))
-
-    jvpp_file = open(os.path.join(callback_facade_package, "CallbackJVpp%sFacadeCallback.java" % plugin_name), 'w')
-    jvpp_file.write(jvpp_facade_callback_template.substitute(inputfile=inputfile,
-                                                             base_package=base_package,
-                                                             plugin_package=plugin_package,
-                                                             plugin_name=plugin_name,
-                                                             dto_package=dto_package,
-                                                             notification_package=notification_package,
-                                                             callback_package=callback_package,
-                                                             methods="".join(callbacks),
-                                                             callback_facade_package=callback_facade_package))
-    jvpp_file.flush()
-    jvpp_file.close()
-
-
-# Returns request name
-def get_request_name(camel_case_dto_name):
-    return remove_suffix(camel_case_dto_name)
-
-
-def remove_suffix(name):
-    if util.is_reply(name):
-        return util.remove_reply_suffix(name)
-    else:
-        if util.is_dump(name):
-            return util.remove_suffix(name, util.dump_suffix)
-        else:
-            return name
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_common_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_common_gen.py
new file mode 100755 (executable)
index 0000000..73615ca
--- /dev/null
@@ -0,0 +1,112 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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 is_array
+
+
+def generate_fields(fields):
+    return "\n".join(_FIELD_TEMPLATE.substitute(type=f.type.java_name_fqn, name=f.java_name) for f in fields)
+
+_FIELD_TEMPLATE = Template("""    public $type $name;""")
+
+
+def generate_hash_code(fields):
+    if len(fields) == 1 and is_array(fields[0]):
+        return _HASH_CODE_SINGLE_ARRAY_TEMPLATE.substitute(array_field=fields[0].java_name)
+    return _HASH_CODE_TEMPLATE.substitute(fields=", ".join(f.java_name for f in fields))
+
+_HASH_CODE_TEMPLATE = Template("""
+    @Override
+    @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
+    public int hashCode() {
+        return java.util.Objects.hash($fields);
+    }""")
+
+_HASH_CODE_SINGLE_ARRAY_TEMPLATE = Template("""
+    @Override
+    @io.fd.vpp.jvpp.coverity.SuppressFBWarnings("UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD")
+    public int hashCode() {
+        return java.util.Arrays.hashCode($array_field);
+    }""")
+
+
+def generate_equals(class_name, fields):
+    comparisons = []
+    for f in fields:
+        if is_array(f):
+            comparisons.append(_EQUALS_ARRAY_FIELD_TEMPLATE.substitute(field_name=f.java_name))
+        else:
+            comparisons.append(_EQUALS_FIELD_TEMPLATE.substitute(field_name=f.java_name))
+
+    if comparisons:
+        comparisons.insert(0, _EQUALS_OTHER_TEMPLATE.substitute(cls_name=class_name))
+    return _EQUALS_TEMPLATE.substitute(comparisons="\n".join(comparisons))
+
+_EQUALS_TEMPLATE = Template("""
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+$comparisons
+
+        return true;
+    }""")
+
+_EQUALS_OTHER_TEMPLATE = Template("""
+        final $cls_name other = ($cls_name) o;
+""")
+
+_EQUALS_FIELD_TEMPLATE = Template("""        if (!java.util.Objects.equals(this.$field_name, other.$field_name)) {
+            return false;
+        }""")
+
+_EQUALS_ARRAY_FIELD_TEMPLATE = Template("""        if (!java.util.Arrays.equals(this.$field_name, other.$field_name)) {
+            return false;
+        }""")
+
+
+def generate_to_string(class_name, fields):
+    to_string = []
+    for f in fields:
+        if is_array(f):
+            to_string.append(_TO_STRING_ARRAY_FIELD_TEMPLATE.substitute(field_name=f.java_name))
+        else:
+            to_string.append(_TO_STRING_FIELD_TEMPLATE.substitute(field_name=f.java_name))
+
+    to_string_fields = " \"}\";"
+    if to_string:
+        to_string_fields = " + \", \" +\n".join(to_string) + " + \"}\";"
+
+    return _TO_STRING_TEMPLATE.substitute(
+        class_name=class_name,
+        to_string_fields=to_string_fields
+    )
+
+_TO_STRING_TEMPLATE = Template("""
+    @Override
+    public String toString() {
+        return "$class_name{" +
+$to_string_fields
+    }""")
+
+_TO_STRING_FIELD_TEMPLATE = Template("""                \"$field_name=\" + $field_name""")
+
+_TO_STRING_ARRAY_FIELD_TEMPLATE = Template(
+    """                \"$field_name=\" + java.util.Arrays.toString($field_name)""")
index c1a7f1c..47a9985 100644 (file)
@@ -1,6 +1,6 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2
 #
-# Copyright (c) 2016 Cisco and/or its affiliates.
+# Copyright (c) 2016,2018 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:
 # 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.
-
-import os
 from string import Template
 
-import dto_gen
-import util
+from jvpp_model import is_control_ping, is_control_ping_reply, is_dump, is_request, is_details, is_reply, is_event
+
+
+def generate_future_facade(work_dir, model, logger):
+    logger.debug("Generating JVpp future facade for %s" % model.json_api_files)
+    _generate_future_jvpp(work_dir, model),
+    _generate_future_jvpp_facade(work_dir, model)
+    _generate_future_jvpp_callback(work_dir, model)
+
+
+def _generate_future_jvpp(work_dir, model):
+    with open("%s/FutureJVpp%s.java" % (work_dir, model.plugin_java_name), "w") as f:
+        f.write(_FUTURE_JVPP_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            json_filename=model.json_api_files,
+            plugin_name=model.plugin_java_name,
+            methods=_generate_future_jvpp_methods(model)
+        ))
+
+_FUTURE_JVPP_TEMPLATE = Template('''
+package $plugin_package.future;
+
+/**
+ * <p>Async facade extension adding specific methods for each request invocation
+ * <br>It was generated by jvpp_future_facade_gen.py based on $json_filename.
+ */
+public interface FutureJVpp${plugin_name} extends io.fd.vpp.jvpp.future.FutureJVppInvoker {
+$methods
+
+    @Override
+    public $plugin_package.notification.${plugin_name}EventRegistry getEventRegistry();
+
+}
+''')
+
+
+def _generate_future_jvpp_methods(model):
+    methods = []
+    for msg in model.messages:
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control_ping managed by jvpp registry.
+            continue
+        reply_name = None
+        if is_request(msg):
+            reply_name = msg.reply_java
+        elif is_dump(msg):
+            # use reply dump wrappers
+            reply_name = "%sReplyDump" % msg.reply_java
+        else:
+            continue
+
+        methods.append(_FUTURE_JVPP_METHOD_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            method_name=msg.java_name_lower,
+            reply_name=reply_name,
+            request_name=msg.java_name_upper
+        ))
+    return "".join(methods)
+
+_FUTURE_JVPP_METHOD_TEMPLATE = Template('''
+    java.util.concurrent.CompletionStage<${plugin_package}.dto.${reply_name}> ${method_name}(${plugin_package}.dto.${request_name} request);
+''')
+
+
+def _generate_future_jvpp_facade(work_dir, model):
+    with open("%s/FutureJVpp%sFacade.java" % (work_dir, model.plugin_java_name), "w") as f:
+        f.write(_FUTURE_JVPP_FACADE_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            json_filename=model.json_api_files,
+            plugin_name=model.plugin_java_name,
+            methods=_generate_future_jvpp_facade_methods(model)
+        ))
 
-jvpp_facade_callback_template = Template("""
-package $plugin_package.$future_package;
+_FUTURE_JVPP_FACADE_TEMPLATE = Template('''
+package $plugin_package.future;
+
+/**
+ * <p>Implementation of FutureJVpp based on AbstractFutureJVppInvoker
+ * <br>It was generated by jvpp_future_facade_gen.py based on $json_filename.
+ */
+public class FutureJVpp${plugin_name}Facade extends io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker implements FutureJVpp${plugin_name} {
+
+    private final $plugin_package.notification.${plugin_name}EventRegistryImpl eventRegistry = new $plugin_package.notification.${plugin_name}EventRegistryImpl();
+
+    /**
+     * <p>Create FutureJVpp${plugin_name}Facade object for provided JVpp instance.
+     * Constructor internally creates FutureJVppFacadeCallback class for processing callbacks
+     * and then connects to provided JVpp instance
+     *
+     * @param jvpp provided io.fd.vpp.jvpp.JVpp instance
+     *
+     * @throws java.io.IOException in case instance cannot connect to JVPP
+     */
+    public FutureJVpp${plugin_name}Facade(final io.fd.vpp.jvpp.JVppRegistry registry, final io.fd.vpp.jvpp.JVpp jvpp) throws java.io.IOException {
+        super(jvpp, registry, new java.util.HashMap<>());
+        java.util.Objects.requireNonNull(registry, "JVppRegistry should not be null");
+        registry.register(jvpp, new FutureJVpp${plugin_name}FacadeCallback(getRequests(), eventRegistry));
+    }
+
+    @Override
+    public $plugin_package.notification.${plugin_name}EventRegistry getEventRegistry() {
+        return eventRegistry;
+    }
+
+$methods
+}
+''')
+
+
+def _generate_future_jvpp_facade_methods(model):
+    methods = []
+    for msg in model.messages:
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control_ping managed by jvpp registry.
+            continue
+        template = None
+        if is_request(msg):
+            template = _FUTURE_JVPP_FACADE_REQUEST_TEMPLATE
+        elif is_dump(msg):
+            template = _FUTURE_JVPP_FACADE_DUMP_TEMPLATE
+        else:
+            continue
+
+        methods.append(template.substitute(
+            plugin_package=model.plugin_package,
+            method_name=msg.java_name_lower,
+            reply_name=msg.reply_java,
+            request_name=msg.java_name_upper
+        ))
+    return "".join(methods)
+
+_FUTURE_JVPP_FACADE_REQUEST_TEMPLATE = Template('''
+    @Override
+    public java.util.concurrent.CompletionStage<${plugin_package}.dto.${reply_name}> ${method_name}(${plugin_package}.dto.${request_name} request) {
+        return send(request);
+    }
+''')
+
+_FUTURE_JVPP_FACADE_DUMP_TEMPLATE = Template('''
+    @Override
+    public java.util.concurrent.CompletionStage<${plugin_package}.dto.${reply_name}ReplyDump> ${method_name}(${plugin_package}.dto.${request_name} request) {
+        return send(request, new ${plugin_package}.dto.${reply_name}ReplyDump());
+    }
+''')
+
+
+def _generate_future_jvpp_callback(work_dir, model):
+    with open("%s/FutureJVpp%sFacadeCallback.java" % (work_dir, model.plugin_java_name), "w") as f:
+        f.write(_FUTURE_JVPP_CALLBACK_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            json_filename=model.json_api_files,
+            plugin_name=model.plugin_java_name,
+            methods=_generate_future_jvpp_callback_methods(model)
+        ))
+
+_FUTURE_JVPP_CALLBACK_TEMPLATE = Template("""
+package $plugin_package.future;
 
 /**
  * <p>Async facade callback setting values to future objects
- * <br>It was generated by jvpp_future_facade_gen.py based on $inputfile
- * <br>(python representation of api file generated by vppapigen).
+ * <br>It was generated by jvpp_future_facade_gen.py based on $json_filename.
  */
-public final class FutureJVpp${plugin_name}FacadeCallback implements $plugin_package.$callback_package.JVpp${plugin_name}GlobalCallback {
+public final class FutureJVpp${plugin_name}FacadeCallback implements $plugin_package.callback.JVpp${plugin_name}GlobalCallback {
 
-    private final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends $base_package.$dto_package.JVppReply<?>>> requests;
-    private final $plugin_package.$notification_package.Global${plugin_name}EventCallback notificationCallback;
+    private final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends io.fd.vpp.jvpp.dto.JVppReply<?>>> requests;
+    private final $plugin_package.notification.Global${plugin_name}EventCallback notificationCallback;
     private static final java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(FutureJVpp${plugin_name}FacadeCallback.class.getName());
 
     public FutureJVpp${plugin_name}FacadeCallback(
-        final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends $base_package.$dto_package.JVppReply<?>>> requestMap,
-        final $plugin_package.$notification_package.Global${plugin_name}EventCallback notificationCallback) {
+        final java.util.Map<java.lang.Integer, java.util.concurrent.CompletableFuture<? extends io.fd.vpp.jvpp.dto.JVppReply<?>>> requestMap,
+        final $plugin_package.notification.Global${plugin_name}EventCallback notificationCallback) {
         this.requests = requestMap;
         this.notificationCallback = notificationCallback;
     }
 
     @Override
     @SuppressWarnings("unchecked")
-    public void onError($base_package.VppCallbackException reply) {
-        final java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<?>> completableFuture;
+    public void onError(io.fd.vpp.jvpp.VppCallbackException reply) {
+        final java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<?>> completableFuture;
 
         synchronized(requests) {
-            completableFuture = (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<?>>) requests.get(reply.getCtxId());
+            completableFuture = (java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<?>>) requests.get(reply.getCtxId());
         }
 
         if(completableFuture != null) {
@@ -60,19 +209,19 @@ public final class FutureJVpp${plugin_name}FacadeCallback implements $plugin_pac
 
     @Override
     @SuppressWarnings("unchecked")
-    public void onControlPingReply(final $base_package.$dto_package.ControlPingReply reply) {
-        java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<?>> completableFuture;
+    public void onControlPingReply(final io.fd.vpp.jvpp.dto.ControlPingReply reply) {
+        java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<?>> completableFuture;
 
         final int replyId = reply.context;
         synchronized(requests) {
-            completableFuture = (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<?>>) requests.get(replyId);
+            completableFuture = (java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<?>>) requests.get(replyId);
 
             if(completableFuture != null) {
                 // Finish dump call
-                if (completableFuture instanceof $base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture) {
-                    completableFuture.complete((($base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture) completableFuture).getReplyDump());
+                if (completableFuture instanceof io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture) {
+                    completableFuture.complete(((io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture) completableFuture).getReplyDump());
                     // Remove future mapped to dump call context id
-                    requests.remove((($base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture) completableFuture).getContextId());
+                    requests.remove(((io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture) completableFuture).getContextId());
                 } else {
                     // reply to regular control ping, complete the future
                     completableFuture.complete(reply);
@@ -91,269 +240,99 @@ $methods
 }
 """)
 
-jvpp_facade_callback_method_template = Template("""
+
+def _generate_future_jvpp_callback_methods(model):
+    methods = []
+    for msg in model.messages:
+        if is_control_ping(msg) or is_control_ping_reply(msg):
+            # Skip control_ping managed by jvpp registry.
+            continue
+        if is_dump(msg) or is_request(msg):
+            continue
+
+        # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client).
+        template = None
+        request_dto = None
+        if is_details(msg):
+            template = _FUTURE_JVPP_FACADE_DETAILS_CALLBACK_TEMPLATE
+            request_dto = msg.request_java
+        elif is_reply(msg):
+            template = _FUTURE_JVPP_FACADE_REPLY_CALLBACK_TEMPLATE
+            request_dto = msg.request_java
+        elif is_event(msg):
+            template = _FUTURE_JVPP_FACADE_EVENT_CALLBACK_TEMPLATE
+        else:
+            raise TypeError("Unknown message type %s", msg)
+
+        methods.append(template.substitute(
+            plugin_package=model.plugin_package,
+            callback_dto=msg.java_name_upper,
+            request_dto=request_dto,
+            callback_dto_field=msg.java_name_lower,
+        ))
+    return "".join(methods)
+
+
+_FUTURE_JVPP_FACADE_DETAILS_CALLBACK_TEMPLATE = Template("""
     @Override
     @SuppressWarnings("unchecked")
-    public void on$callback_dto(final $plugin_package.$dto_package.$callback_dto reply) {
-        java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<$plugin_package.$dto_package.$request_dto>> completableFuture;
+    public void on$callback_dto(final $plugin_package.dto.$callback_dto reply) {
+        io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.dto.${callback_dto}ReplyDump> completableFuture;
         final int replyId = reply.context;
         if (LOG.isLoggable(java.util.logging.Level.FINE)) {
             LOG.fine(String.format("Received $callback_dto event message: %s", reply));
         }
         synchronized(requests) {
-            completableFuture =
-            (java.util.concurrent.CompletableFuture<$base_package.$dto_package.JVppReply<$plugin_package.$dto_package.$request_dto>>) requests.get(replyId);
+            completableFuture = (io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.dto.${callback_dto}ReplyDump>) requests.get(replyId);
 
-            if(completableFuture != null) {
-                // received reply on request, complete future created by sender and remove it from map
-                completableFuture.complete(reply);
-                requests.remove(replyId);
-            } else {
+            if(completableFuture == null) {
                 // reply received before writer created future,
-                // create new future, complete it and put into map to
-                // notify sender that reply is already received
-                completableFuture = new  java.util.concurrent.CompletableFuture<>();
-                completableFuture.complete(reply);
+                // create new future, and put into map to notify sender that reply is already received,
+                // following details replies will add information to this future
+                completableFuture = new io.fd.vpp.jvpp.future.AbstractFutureJVppInvoker.CompletableDumpFuture<>(replyId,
+                    new $plugin_package.dto.${callback_dto}ReplyDump());
                 requests.put(replyId, completableFuture);
             }
+            completableFuture.getReplyDump().$callback_dto_field.add(reply);
         }
     }
 """)
 
-jvpp_facade_callback_notification_method_template = Template("""
-    @Override
-    public void on$callback_dto($plugin_package.$dto_package.$callback_dto notification) {
-        if (LOG.isLoggable(java.util.logging.Level.FINE)) {
-            LOG.fine(String.format("Received $callback_dto event message: %s", notification));
-        }
-        notificationCallback.on$callback_dto(notification);
-    }
-""")
-
-jvpp_facade_details_callback_method_template = Template("""
+_FUTURE_JVPP_FACADE_REPLY_CALLBACK_TEMPLATE = Template("""
     @Override
     @SuppressWarnings("unchecked")
-    public void on$callback_dto(final $plugin_package.$dto_package.$callback_dto reply) {
-        $base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.$dto_package.$callback_dto_reply_dump> completableFuture;
+    public void on$callback_dto(final $plugin_package.dto.$callback_dto reply) {
+        java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<$plugin_package.dto.$request_dto>> completableFuture;
         final int replyId = reply.context;
         if (LOG.isLoggable(java.util.logging.Level.FINE)) {
             LOG.fine(String.format("Received $callback_dto event message: %s", reply));
         }
         synchronized(requests) {
-            completableFuture = ($base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture<$plugin_package.$dto_package.$callback_dto_reply_dump>) requests.get(replyId);
+            completableFuture =
+            (java.util.concurrent.CompletableFuture<io.fd.vpp.jvpp.dto.JVppReply<$plugin_package.dto.$request_dto>>) requests.get(replyId);
 
-            if(completableFuture == null) {
+            if(completableFuture != null) {
+                // received reply on request, complete future created by sender and remove it from map
+                completableFuture.complete(reply);
+                requests.remove(replyId);
+            } else {
                 // reply received before writer created future,
-                // create new future, and put into map to notify sender that reply is already received,
-                // following details replies will add information to this future
-                completableFuture = new $base_package.$future_package.AbstractFutureJVppInvoker.CompletableDumpFuture<>(replyId,
-                    new $plugin_package.$dto_package.$callback_dto_reply_dump());
+                // create new future, complete it and put into map to
+                // notify sender that reply is already received
+                completableFuture = new  java.util.concurrent.CompletableFuture<>();
+                completableFuture.complete(reply);
                 requests.put(replyId, completableFuture);
             }
-            completableFuture.getReplyDump().$callback_dto_field.add(reply);
         }
     }
 """)
 
-
-def generate_jvpp(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package,
-                  notification_package, future_facade_package, inputfile, logger):
-    """ Generates JVpp interface and JNI implementation """
-    logger.debug("Generating JVpp future facade for %s" % inputfile)
-
-    if not os.path.exists(future_facade_package):
-        os.mkdir(future_facade_package)
-
-    methods = []
-    methods_impl = []
-
-    # Generate methods for sending messages.
-    for func in func_list:
-        camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name'])
-
-        if util.is_control_ping(camel_case_name_with_suffix):
-            # Skip control ping managed by jvpp registry.
-            continue
-
-        # Process replies instead of requests (optimization).
-        if not util.is_reply(camel_case_name_with_suffix):
-            # Do not generate send methods for messages that do not have replies.
-            continue
-
-        camel_case_request_method_name = util.remove_reply_suffix(util.underscore_to_camelcase(func['name']))
-        if util.is_details(camel_case_name_with_suffix):
-            camel_case_reply_name = util.underscore_to_camelcase_upper(func['name'])
-            methods.append(future_jvpp_method_template.substitute(plugin_package=plugin_package,
-                                                                  dto_package=dto_package,
-                                                                  method_name=camel_case_request_method_name +
-                                                                              util.underscore_to_camelcase_upper(util.dump_suffix),
-                                                                  reply_name=camel_case_reply_name + dto_gen.dump_dto_suffix,
-                                                                  request_name=util.remove_reply_suffix(camel_case_reply_name) +
-                                                                               util.underscore_to_camelcase_upper(util.dump_suffix)))
-            methods_impl.append(future_jvpp_dump_method_impl_template.substitute(plugin_package=plugin_package,
-                                                                                 dto_package=dto_package,
-                                                                                 method_name=camel_case_request_method_name +
-                                                                                             util.underscore_to_camelcase_upper(util.dump_suffix),
-                                                                                 reply_name=camel_case_reply_name + dto_gen.dump_dto_suffix,
-                                                                                 request_name=util.remove_reply_suffix(camel_case_reply_name) +
-                                                                                              util.underscore_to_camelcase_upper(util.dump_suffix)))
-        else:
-            request_name = util.remove_reply_suffix(camel_case_name_with_suffix)
-
-            methods.append(future_jvpp_method_template.substitute(plugin_package=plugin_package,
-                                                                  dto_package=dto_package,
-                                                                  method_name=camel_case_request_method_name,
-                                                                  reply_name=camel_case_name_with_suffix,
-                                                                  request_name=request_name))
-            methods_impl.append(future_jvpp_method_impl_template.substitute(plugin_package=plugin_package,
-                                                                            dto_package=dto_package,
-                                                                            method_name=camel_case_request_method_name,
-                                                                            reply_name=camel_case_name_with_suffix,
-                                                                            request_name=request_name))
-
-    jvpp_file = open(os.path.join(future_facade_package, "FutureJVpp%s.java" % plugin_name), 'w')
-    jvpp_file.write(future_jvpp_template.substitute(inputfile=inputfile,
-                                                    base_package=base_package,
-                                                    plugin_package=plugin_package,
-                                                    plugin_name=plugin_name,
-                                                    notification_package=notification_package,
-                                                    methods="".join(methods),
-                                                    future_package=future_facade_package))
-    jvpp_file.flush()
-    jvpp_file.close()
-
-    jvpp_file = open(os.path.join(future_facade_package, "FutureJVpp%sFacade.java" % plugin_name), 'w')
-    jvpp_file.write(future_jvpp_facade_template.substitute(inputfile=inputfile,
-                                                           base_package=base_package,
-                                                           plugin_package=plugin_package,
-                                                           plugin_name=plugin_name,
-                                                           dto_package=dto_package,
-                                                           notification_package=notification_package,
-                                                           methods="".join(methods_impl),
-                                                           future_package=future_facade_package))
-    jvpp_file.flush()
-    jvpp_file.close()
-
-    generate_callback(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, notification_package, future_facade_package, inputfile)
-
-
-future_jvpp_template = Template('''
-package $plugin_package.$future_package;
-
-/**
- * <p>Async facade extension adding specific methods for each request invocation
- * <br>It was generated by jvpp_future_facade_gen.py based on $inputfile
- * <br>(python representation of api file generated by vppapigen).
- */
-public interface FutureJVpp${plugin_name} extends $base_package.$future_package.FutureJVppInvoker {
-$methods
-
-    @Override
-    public $plugin_package.$notification_package.${plugin_name}EventRegistry getEventRegistry();
-
-}
-''')
-
-future_jvpp_method_template = Template('''
-    java.util.concurrent.CompletionStage<$plugin_package.$dto_package.$reply_name> $method_name($plugin_package.$dto_package.$request_name request);
-''')
-
-
-future_jvpp_facade_template = Template('''
-package $plugin_package.$future_package;
-
-/**
- * <p>Implementation of FutureJVpp based on AbstractFutureJVppInvoker
- * <br>It was generated by jvpp_future_facade_gen.py based on $inputfile
- * <br>(python representation of api file generated by vppapigen).
- */
-public class FutureJVpp${plugin_name}Facade extends $base_package.$future_package.AbstractFutureJVppInvoker implements FutureJVpp${plugin_name} {
-
-    private final $plugin_package.$notification_package.${plugin_name}EventRegistryImpl eventRegistry = new $plugin_package.$notification_package.${plugin_name}EventRegistryImpl();
-
-    /**
-     * <p>Create FutureJVpp${plugin_name}Facade object for provided JVpp instance.
-     * Constructor internally creates FutureJVppFacadeCallback class for processing callbacks
-     * and then connects to provided JVpp instance
-     *
-     * @param jvpp provided $base_package.JVpp instance
-     *
-     * @throws java.io.IOException in case instance cannot connect to JVPP
-     */
-    public FutureJVpp${plugin_name}Facade(final $base_package.JVppRegistry registry, final $base_package.JVpp jvpp) throws java.io.IOException {
-        super(jvpp, registry, new java.util.HashMap<>());
-        java.util.Objects.requireNonNull(registry, "JVppRegistry should not be null");
-        registry.register(jvpp, new FutureJVpp${plugin_name}FacadeCallback(getRequests(), eventRegistry));
-    }
-
+_FUTURE_JVPP_FACADE_EVENT_CALLBACK_TEMPLATE = Template("""
     @Override
-    public $plugin_package.$notification_package.${plugin_name}EventRegistry getEventRegistry() {
-        return eventRegistry;
-    }
-
-$methods
-}
-''')
-
-future_jvpp_method_impl_template = Template('''
-    @Override
-    public java.util.concurrent.CompletionStage<$plugin_package.$dto_package.$reply_name> $method_name($plugin_package.$dto_package.$request_name request) {
-        return send(request);
-    }
-''')
-
-future_jvpp_dump_method_impl_template = Template('''
-    @Override
-    public java.util.concurrent.CompletionStage<$plugin_package.$dto_package.$reply_name> $method_name($plugin_package.$dto_package.$request_name request) {
-        return send(request, new $plugin_package.$dto_package.$reply_name());
+    public void on$callback_dto($plugin_package.dto.$callback_dto notification) {
+        if (LOG.isLoggable(java.util.logging.Level.FINE)) {
+            LOG.fine(String.format("Received $callback_dto event message: %s", notification));
+        }
+        notificationCallback.on$callback_dto(notification);
     }
-''')
-
-
-def generate_callback(func_list, base_package, plugin_package, plugin_name, dto_package, callback_package, notification_package, future_facade_package, inputfile):
-    callbacks = []
-    for func in func_list:
-        camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name'])
-
-        if util.is_control_ping(camel_case_name_with_suffix):
-            # Skip control ping managed by jvpp registry.
-            continue
-        if util.is_dump(func['name']) or util.is_request(func['name'], func_list):
-            continue
-
-        # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client).
-        if util.is_details(camel_case_name_with_suffix):
-            # Callbacks for detail messages that append replies to a list.
-            camel_case_method_name = util.underscore_to_camelcase(func['name'])
-            camel_case_reply_name = util.underscore_to_camelcase_upper(func['name'])
-            callbacks.append(jvpp_facade_details_callback_method_template.substitute(base_package=base_package,
-                                                                                     plugin_package=plugin_package,
-                                                                                     dto_package=dto_package,
-                                                                                     callback_dto=camel_case_name_with_suffix,
-                                                                                     callback_dto_field=camel_case_method_name,
-                                                                                     callback_dto_reply_dump=camel_case_reply_name + dto_gen.dump_dto_suffix,
-                                                                                     future_package=future_facade_package))
-        elif util.is_reply(camel_case_name_with_suffix):
-            request_dto = util.remove_reply_suffix(util.underscore_to_camelcase_upper(func['name']))
-            callbacks.append(jvpp_facade_callback_method_template.substitute(base_package=base_package,
-                                                                             plugin_package=plugin_package,
-                                                                             dto_package=dto_package,
-                                                                             callback_dto=camel_case_name_with_suffix,
-                                                                             request_dto=request_dto))
-        else:
-            callbacks.append(jvpp_facade_callback_notification_method_template.substitute(plugin_package=plugin_package,
-                                                                                          dto_package=dto_package,
-                                                                                          callback_dto=camel_case_name_with_suffix))
-
-    jvpp_file = open(os.path.join(future_facade_package, "FutureJVpp%sFacadeCallback.java" % plugin_name), 'w')
-    jvpp_file.write(jvpp_facade_callback_template.substitute(inputfile=inputfile,
-                                                             base_package=base_package,
-                                                             plugin_package=plugin_package,
-                                                             plugin_name=plugin_name,
-                                                             dto_package=dto_package,
-                                                             notification_package=notification_package,
-                                                             callback_package=callback_package,
-                                                             methods="".join(callbacks),
-                                                             future_package=future_facade_package))
-    jvpp_file.flush()
-    jvpp_file.close()
+""")
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_ifc_gen.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_ifc_gen.py
new file mode 100755 (executable)
index 0000000..e2b2922
--- /dev/null
@@ -0,0 +1,71 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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 is_request, is_dump, is_event
+
+
+def generate_java_ifc(work_dir, model, logger):
+    logger.debug("Generating JVpp interface for %s" % model.json_api_files)
+    messages = filter(_jvpp_ifc_filter, model.messages)
+    plugin_package = model.plugin_package
+    methods = []
+    for msg in messages:
+        if msg.has_fields:
+            methods.append(_JVPP_IFC_METHOD_TEMPLATE.substitute(
+                name=msg.java_name_lower,
+                plugin_package=plugin_package,
+                type=msg.java_name_upper))
+        else:
+            methods.append(_JVPP_IFC_NO_ARG_METHOD_TEMPLATE.substitute(name=msg.java_name_lower))
+
+    plugin_name = model.plugin_java_name
+    jvpp_interface = _JVPP_IFC_TEMPLATE.substitute(
+        plugin_package=plugin_package,
+        json_filename=model.json_api_files,
+        plugin_name=plugin_name,
+        methods="\n".join(methods)
+    )
+    with open("%s/JVpp%s.java" % (work_dir, plugin_name), "w") as f:
+        f.write(jvpp_interface)
+
+
+def _jvpp_ifc_filter(msg):
+    return is_request(msg) or is_dump(msg) or is_event(msg)
+
+
+_JVPP_IFC_METHOD_TEMPLATE = Template(
+    """    int $name($plugin_package.dto.$type request) throws io.fd.vpp.jvpp.VppInvocationException;""")
+
+_JVPP_IFC_NO_ARG_METHOD_TEMPLATE = Template("""    int $name() throws io.fd.vpp.jvpp.VppInvocationException;""")
+
+_JVPP_IFC_TEMPLATE = Template("""package $plugin_package;
+
+/**
+ * <p>Java representation of plugin's api file.
+ * <br>It was generated by jvpp_impl_gen.py based on $json_filename.
+ * <br>(python representation of api file generated by vppapigen)
+ */
+public interface JVpp${plugin_name} extends io.fd.vpp.jvpp.JVpp {
+    /**
+     * Generic dispatch method for sending requests to VPP
+     *
+     * @throws io.fd.vpp.jvpp.VppInvocationException if send request had failed
+     */
+    int send(io.fd.vpp.jvpp.dto.JVppRequest request) throws io.fd.vpp.jvpp.VppInvocationException;
+$methods
+}
+""")
old mode 100644 (file)
new mode 100755 (executable)
index 69eb67e..aff8d7f
@@ -1,6 +1,6 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2
 #
-# Copyright (c) 2016 Cisco and/or its affiliates.
+# Copyright (c) 2016,2018 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:
 # 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.
-
-import util
+#
 from string import Template
 
-jvpp_ifc_template = Template("""
-package $plugin_package;
+from jvpp_model import is_request, is_dump, is_event
 
-/**
- * <p>Java representation of plugin's api file.
- * <br>It was generated by jvpp_impl_gen.py based on $inputfile
- * <br>(python representation of api file generated by vppapigen).
- */
-public interface JVpp${plugin_name} extends $base_package.JVpp {
 
-    /**
-     * Generic dispatch method for sending requests to VPP
-     *
-     * @throws io.fd.vpp.jvpp.VppInvocationException if send request had failed
-     */
-    int send($base_package.$dto_package.JVppRequest request) throws io.fd.vpp.jvpp.VppInvocationException;
+def generate_java_impl(work_dir, model, logger):
+    logger.debug("Generating JVpp implementation for %s" % model.json_api_files)
+    messages = filter(_jvpp_impl_filter, model.messages)
+    plugin_package = model.plugin_package
+    methods = []
+    for msg in messages:
+        if msg.has_fields:
+            methods.append(_JVPP_IMPL_METHOD_TEMPLATE.substitute(
+                name=msg.java_name_lower,
+                plugin_package=plugin_package,
+                type=msg.java_name_upper))
+        else:
+            methods.append(_JVPP_IMPL_NO_ARG_METHOD_TEMPLATE.substitute(
+                name=msg.java_name_lower,
+                type=msg.java_name_upper))
+
+    plugin_name = model.plugin_java_name
+    jvpp_impl = _JVPP_IMPL_TEMPLATE.substitute(
+        plugin_package=plugin_package,
+        json_filename=model.json_api_files,
+        plugin_name=model.plugin_java_name,
+        plugin_name_underscore=model.plugin_name,
+        methods="\n".join(methods))
 
-$methods
-}
-""")
+    with open("%s/JVpp%sImpl.java" % (work_dir, plugin_name), "w") as f:
+        f.write(jvpp_impl)
+
+
+def _jvpp_impl_filter(msg):
+    return is_request(msg) or is_dump(msg) or is_event(msg)
 
-jvpp_impl_template = Template("""
-package $plugin_package;
+
+_JVPP_IMPL_TEMPLATE = Template("""package $plugin_package;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -50,14 +62,14 @@ import java.nio.file.attribute.PosixFilePermissions;
 import java.util.Set;
 import java.util.logging.Logger;
 import java.util.logging.Level;
-import $base_package.callback.JVppCallback;
-import $base_package.VppConnection;
-import $base_package.JVppRegistry;
+import io.fd.vpp.jvpp.callback.JVppCallback;
+import io.fd.vpp.jvpp.VppConnection;
+import io.fd.vpp.jvpp.JVppRegistry;
 
 /**
  * <p>Default implementation of JVpp interface.
- * <br>It was generated by jvpp_impl_gen.py based on $inputfile
- * <br>(python representation of api file generated by vppapigen).
+ * <br>It was generated by jvpp_impl_gen.py based on $json_filename.
+ * <br>(python representation of api file generated by vppapigen)
  */
 public final class JVpp${plugin_name}Impl implements $plugin_package.JVpp${plugin_name} {
 
@@ -121,7 +133,7 @@ public final class JVpp${plugin_name}Impl implements $plugin_package.JVpp${plugi
     }
 
     @Override
-    public int send($base_package.$dto_package.JVppRequest request) throws io.fd.vpp.jvpp.VppInvocationException {
+    public int send(io.fd.vpp.jvpp.dto.JVppRequest request) throws io.fd.vpp.jvpp.VppInvocationException {
         return request.send(this);
     }
 
@@ -129,91 +141,33 @@ public final class JVpp${plugin_name}Impl implements $plugin_package.JVpp${plugi
     public final int controlPing(final io.fd.vpp.jvpp.dto.ControlPing controlPing) throws io.fd.vpp.jvpp.VppInvocationException {
         return registry.controlPing(JVpp${plugin_name}Impl.class);
     }
-
 $methods
 }
 """)
 
-method_template = Template("""    int $name($plugin_package.$dto_package.$request request) throws io.fd.vpp.jvpp.VppInvocationException;""")
-method_native_template = Template(
-    """    private static native int ${name}0($plugin_package.$dto_package.$request request);""")
-method_impl_template = Template("""    public final int $name($plugin_package.$dto_package.$request request) throws io.fd.vpp.jvpp.VppInvocationException {
-        java.util.Objects.requireNonNull(request,"Null request object");
+_JVPP_IMPL_METHOD_TEMPLATE = Template("""
+    private static native int ${name}0($plugin_package.dto.$type request);
+    public final int $name($plugin_package.dto.$type request) throws io.fd.vpp.jvpp.VppInvocationException {
+        java.util.Objects.requireNonNull(request, "Null request object");
         connection.checkActive();
-        if(LOG.isLoggable(Level.FINE)) {
-            LOG.fine(String.format("Sending $name event message: %s", request));
+        if (LOG.isLoggable(Level.FINE)) {
+            LOG.fine(String.format("Sending $type event message: %s", request));
         }
         int result=${name}0(request);
-        if(result<0){
-            throw new io.fd.vpp.jvpp.VppInvocationException("${name}",result);
+        if (result<0){
+            throw new io.fd.vpp.jvpp.VppInvocationException("${name}", result);
         }
         return result;
-    }
-""")
+    }""")
 
-no_arg_method_template = Template("""    int $name() throws io.fd.vpp.jvpp.VppInvocationException;""")
-no_arg_method_native_template = Template("""    private static native int ${name}0() throws io.fd.vpp.jvpp.VppInvocationException;""")
-no_arg_method_impl_template = Template("""    public final int $name() throws io.fd.vpp.jvpp.VppInvocationException {
+_JVPP_IMPL_NO_ARG_METHOD_TEMPLATE = Template("""
+    private static native int ${name}0() throws io.fd.vpp.jvpp.VppInvocationException;
+    public final int $name() throws io.fd.vpp.jvpp.VppInvocationException {
         connection.checkActive();
-        LOG.fine("Sending $name event message");
+        LOG.fine("Sending $type event message");
         int result=${name}0();
         if(result<0){
-            throw new io.fd.vpp.jvpp.VppInvocationException("${name}",result);
+            throw new io.fd.vpp.jvpp.VppInvocationException("${name}", result);
         }
         return result;
-    }
-""")
-
-
-def generate_jvpp(func_list, base_package, plugin_package, plugin_name_underscore, dto_package, inputfile, logger):
-    """ Generates JVpp interface and JNI implementation """
-    logger.debug("Generating JVpp interface implementation for %s" % inputfile)
-    plugin_name = util.underscore_to_camelcase_upper(plugin_name_underscore)
-
-    methods = []
-    methods_impl = []
-    for func in func_list:
-        camel_case_name = util.underscore_to_camelcase(func['name'])
-        camel_case_name_upper = util.underscore_to_camelcase_upper(func['name'])
-        if util.is_reply(camel_case_name):
-            continue
-
-        if len(func['args']) == 0:
-            methods.append(no_arg_method_template.substitute(name=camel_case_name))
-            methods_impl.append(no_arg_method_native_template.substitute(name=camel_case_name))
-            methods_impl.append(no_arg_method_impl_template.substitute(name=camel_case_name))
-        else:
-            methods.append(method_template.substitute(name=camel_case_name,
-                                                      request=camel_case_name_upper,
-                                                      plugin_package=plugin_package,
-                                                      dto_package=dto_package))
-            methods_impl.append(method_native_template.substitute(name=camel_case_name,
-                                                                  request=camel_case_name_upper,
-                                                                  plugin_package=plugin_package,
-                                                                  dto_package=dto_package))
-            methods_impl.append(method_impl_template.substitute(name=camel_case_name,
-                                                                request=camel_case_name_upper,
-                                                                plugin_package=plugin_package,
-                                                                dto_package=dto_package))
-
-    jvpp_file = open("JVpp%s.java" % plugin_name, 'w')
-    jvpp_file.write(
-        jvpp_ifc_template.substitute(inputfile=inputfile,
-                                     methods="\n".join(methods),
-                                     base_package=base_package,
-                                     plugin_package=plugin_package,
-                                     plugin_name=plugin_name,
-                                     dto_package=dto_package))
-    jvpp_file.flush()
-    jvpp_file.close()
-
-    jvpp_file = open("JVpp%sImpl.java" % plugin_name, 'w')
-    jvpp_file.write(jvpp_impl_template.substitute(inputfile=inputfile,
-                                                  methods="\n".join(methods_impl),
-                                                  base_package=base_package,
-                                                  plugin_package=plugin_package,
-                                                  plugin_name=plugin_name,
-                                                  plugin_name_underscore=plugin_name_underscore,
-                                                  dto_package=dto_package))
-    jvpp_file.flush()
-    jvpp_file.close()
+    }""")
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_model.py b/src/vpp-api/java/jvpp/gen/jvppgen/jvpp_model.py
new file mode 100755 (executable)
index 0000000..3b09a81
--- /dev/null
@@ -0,0 +1,467 @@
+#!/usr/bin/env python2
+#
+# Copyright (c) 2018 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.
+#
+import json
+import pprint
+from collections import OrderedDict
+
+BASE_PACKAGE = "io.fd.vpp.jvpp"
+
+
+class ParseException(Exception):
+    pass
+
+
+class Type(object):
+    def __init__(self, name, java_name, java_name_fqn, jni_signature, jni_type, jni_accessor,
+                 host_to_net_function, net_to_host_function):
+        """
+        Initializes Type class.
+
+        :param name: name of type as defined in .api file, e.g. u8, u32[] or mac_entry
+        :param java_name: corresponding java name, e.g. byte, int[] or MacEntry
+        :param java_name_fqn: fully qualified java name, e.g. io.fd.vpp.jvpp.core.types.MacEntry
+        :param jni_signature: JNI Type signature, e.g. B, [I or Lio.fd.vpp.jvpp.core.types.MacEntry;
+                              See https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#type_signatures
+        :param jni_type: JNI reference type, e.g. jbyte jintArray, jobject
+                         See https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#reference_types
+        :param jni_accessor: Java type do by used in Get<type>Field, Set<type>Field and other functions.
+                             See https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#accessing_fields_of_objects
+        :param host_to_net_function: name of function host to net byte order swap function
+        :param net_to_host_function: name of function net to host byte order swap function
+        """
+        self.name = name
+        self.java_name = java_name
+
+        # Java generation specific properties, TODO(VPP-1186): move to Java specific subclass
+        self.java_name_fqn = java_name_fqn
+
+        # JNI generation specific properties, TODO(VPP-1186): move to JNI specific subclass
+        self.jni_signature = jni_signature
+
+        # Native type, see:
+        # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#primitive_types
+        # and
+        # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#reference_types
+        self.jni_type = jni_type
+
+        # Java type do by used in Get<type>Field, Set<type>Field and other functions, see:
+        # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#accessing_fields_of_objects
+        self.jni_accessor = jni_accessor
+
+        self.host_to_net_function = host_to_net_function
+        self.net_to_host_function = net_to_host_function
+        self.is_swap_needed = host_to_net_function and net_to_host_function
+
+
+class SimpleType(Type):
+    def __init__(self, name, java_name, jni_signature, jni_type, jni_accessor,
+                 host_to_net_function=None, net_to_host_function=None):
+        super(SimpleType, self).__init__(
+            name=name,
+            java_name=java_name,
+            java_name_fqn=java_name,
+            jni_signature=jni_signature,
+            jni_type=jni_type,
+            jni_accessor=jni_accessor,
+            host_to_net_function=host_to_net_function,
+            net_to_host_function=net_to_host_function
+        )
+        self.vpp_name = name
+
+    def get_host_to_net_function(self, host_ref_name, net_ref_name):
+        return "%s = %s(%s)" % (net_ref_name, self.host_to_net_function, host_ref_name)
+
+    def __str__(self):
+        return "SimpleType{name:%s, java_name:%s}" % (self.name, self.java_name)
+
+
+# TODO(VPP-1187): add array host to net functions to reduce number of members and simplify JNI generation
+class Array(Type):
+    def __init__(self, base_type):
+        super(Array, self).__init__(
+            name=base_type.name + _ARRAY_SUFFIX,
+            java_name=base_type.java_name + _ARRAY_SUFFIX,
+            java_name_fqn=base_type.java_name_fqn + _ARRAY_SUFFIX,
+            jni_signature="[%s" % base_type.jni_signature,
+            jni_type="%sArray" % base_type.jni_type,
+            jni_accessor="Object",
+            host_to_net_function=base_type.host_to_net_function,
+            net_to_host_function=base_type.net_to_host_function
+        )
+        self.base_type = base_type
+
+    def get_host_to_net_function(self, host_ref_name, net_ref_name):
+        return self.base_type.get_host_to_net_function(host_ref_name, net_ref_name)
+
+    def __str__(self):
+        return "Array{name:%s, java_name:%s}" % (self.name, self.java_name)
+
+
+class Class(Type):
+    def __init__(self, name, crc, fields, definition, plugin_name):
+        _java_name = _underscore_to_camelcase_upper(name)
+
+        super(Class, 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.crc = crc
+        self.fields = fields
+        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 Field(object):
+    def __init__(self, name, field_type, array_len=None, array_len_field=None):
+        self.name = name
+        self.java_name = _underscore_to_camelcase_lower(name)
+        self.type = field_type
+        self.array_len = array_len
+        self.array_len_field = array_len_field
+
+    def __str__(self):
+        return "Field{name:%s, java_name:%s, type:%s}" % (self.name, self.java_name, self.type)
+
+
+class Message(object):
+    def __init__(self, name, crc, fields, definition):
+        self.name = name
+        self.java_name_upper = _underscore_to_camelcase_upper(name)
+        self.java_name_lower = _underscore_to_camelcase_lower(name)
+        self.crc = crc[2:]
+        self.fields = fields
+        self.has_fields = fields != []
+        self.doc = _message_to_javadoc(definition)
+
+    def __str__(self):
+        return "Message{name:%s, java_name:%s, crc:%s, fields:%s}" % (
+            self.name, self.java_name_upper, self.crc, self.fields)
+
+
+class Event(Message):
+    def __init__(self, name, crc, fields, definition):
+        super(Event, self).__init__(name, crc, fields, definition)
+
+
+class Request(Message):
+    def __init__(self, name, reply, crc, fields, definition):
+        super(Request, self).__init__(name, crc, fields, definition)
+        self.reply = reply
+        self.reply_java = _underscore_to_camelcase_upper(reply)
+
+    def __str__(self):
+        return "Request{name:%s, reply:%s, crc:%s, fields:%s}" % (self.name, self.reply, self.crc, self.fields)
+
+
+class Reply(Message):
+    def __init__(self, name, request, crc, fields, definition):
+        super(Reply, self).__init__(name, crc, fields, definition)
+        self.request = request
+        self.request_java = _underscore_to_camelcase_upper(request)
+
+    def __str__(self):
+        return "Reply{name:%s, request:%s, crc:%s, fields:%s}" % (self.name, self.request, self.crc, self.fields)
+
+
+class Dump(Message):
+    def __init__(self, name, details, crc, fields, definition):
+        super(Dump, self).__init__(name, crc, fields, definition)
+        self.details = details
+        self.reply_java = _underscore_to_camelcase_upper(details)
+
+    def __str__(self):
+        return "Dump{name:%s, details:%s, crc:%s, fields:%s}" % (self.name, self.details, self.crc, self.fields)
+
+
+class Details(Message):
+    def __init__(self, name, dump, crc, fields, definition):
+        super(Details, self).__init__(name, crc, fields, definition)
+        self.dump = dump
+        self.request_java = _underscore_to_camelcase_upper(dump)
+
+    def __str__(self):
+        return "Details{name:%s, dump:%s, crc:%s, fields:%s}" % (self.name, self.dump, self.crc, self.fields)
+
+
+def is_retval(field):
+    return field.name == u'retval'
+
+
+def is_array(field):
+    return field.array_len is not None
+
+
+def is_request(msg):
+    return hasattr(msg, 'reply')
+
+
+def is_reply(msg):
+    return hasattr(msg, 'request')
+
+
+def is_dump(msg):
+    return hasattr(msg, 'details')
+
+
+def is_details(msg):
+    return hasattr(msg, 'dump')
+
+
+def is_event(msg):
+    return isinstance(msg, Event)
+
+
+def is_control_ping(msg):
+    return msg.name == u'control_ping'
+
+
+def is_control_ping_reply(msg):
+    return msg.name == u'control_ping_reply'
+
+
+class JVppModel(object):
+    def __init__(self, logger, json_api_files, plugin_name):
+        self.logger = logger
+        # TODO(VPP-1188): provide json_file_by_definition map to improve javadoc
+        self.json_api_files = json_api_files
+        self.plugin_package = BASE_PACKAGE + "." + plugin_name
+        self.plugin_name = plugin_name
+        self.plugin_java_name = _underscore_to_camelcase_upper(plugin_name)
+        self._load_json_files(json_api_files)
+        self._parse_types()
+        self._parse_services()
+        self._parse_messages()
+        self._validate_messages()
+
+    def _load_json_files(self, json_api_files):
+        self._enums = []
+        self._types = []
+        self._messages = []
+        self._services = {}
+        for file_name in json_api_files:
+            with open(file_name) as f:
+                j = json.load(f)
+                self._enums.extend(j['enums'])
+                self._types.extend(j['types'])
+                self._messages.extend(j['messages'])
+                self._services.update(j['services'])
+
+    def _parse_types(self):
+        # Mapping according to:
+        # http://docs.oracle.com/javase/7/do+'[]'cs/technotes/guides/jni/spec/types.html
+        # and
+        # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#Get_type_Field_routines
+        #
+        # Unsigned types are converted to signed java types that have the same size.
+        # It is the API user responsibility to interpret them correctly.
+
+        self._types_by_name = OrderedDict({
+            'u8': SimpleType('u8', 'byte', 'B', 'jbyte', 'Byte'),
+            'i8': SimpleType('i8', 'byte', 'B', 'jbyte', 'Byte'),
+            'u16': SimpleType('u16', 'short', 'S', 'jshort', 'Short',
+                              host_to_net_function='clib_host_to_net_u16',
+                              net_to_host_function='clib_net_to_host_u16'),
+            'i16': SimpleType('i16', 'short', 'S', 'jshort', 'Short',
+                              host_to_net_function='clib_host_to_net_i16',
+                              net_to_host_function='clib_net_to_host_i16'),
+            'u32': SimpleType('u32', 'int', 'I', 'jint', 'Int',
+                              host_to_net_function='clib_host_to_net_u32',
+                              net_to_host_function='clib_net_to_host_u32'),
+            'i32': SimpleType('i32', 'int', 'I', 'jint', 'Int',
+                              host_to_net_function='clib_host_to_net_i32',
+                              net_to_host_function='clib_net_to_host_i32'),
+            'u64': SimpleType('u64', 'long', 'J', 'jlong', 'Long',
+                              host_to_net_function='clib_host_to_net_u64',
+                              net_to_host_function='clib_net_to_host_u64'),
+            'i64': SimpleType('i64', 'long', 'J', 'jlong', 'Long',
+                              host_to_net_function='clib_host_to_net_i64',
+                              net_to_host_function='clib_net_to_host_i64'),
+            'f64': SimpleType('f64', 'double', 'D', 'jdouble', 'Double')
+        })
+
+        for n, t in self._types_by_name.items():
+            self._types_by_name[n + _ARRAY_SUFFIX] = Array(t)
+
+        for json_type in self._types:
+            name = json_type[0]
+            definition = json_type[1:]
+            _type = self._parse_type(name, definition)
+            self._types_by_name[name] = _type
+            self._types_by_name[name + _ARRAY_SUFFIX] = Array(_type)
+
+        self.types = self._types_by_name.values()
+
+    def _parse_type(self, name, definition):
+        self.logger.debug("Parsing type %s: %s", name, definition)
+        crc, fields = self._parse_fields(definition)
+        return Class(name, crc, fields, definition, self.plugin_name)
+
+    def _parse_services(self):
+        self._dumps_by_details = {}
+        self._requests_by_reply = {}
+        for name, service in self._services.iteritems():
+            if _is_stream(service):
+                self._dumps_by_details[service['reply']] = name
+            else:
+                self._requests_by_reply[service['reply']] = name
+
+    def _parse_messages(self):
+        # Preserve ordering from JSON file to make debugging easier.
+        self._messages_by_name = OrderedDict()
+        for msg in self._messages:
+            try:
+                name = msg[0]
+                definition = msg[1:]
+                self._messages_by_name[name] = self._parse_message(name, definition)
+            except ParseException as e:
+                self.logger.warning("Failed to parse message %s: %s. Skipping message.", name, e)
+
+    def _parse_message(self, name, definition):
+        self.logger.debug("Parsing message %s: %s", name, definition)
+        crc, fields = self._parse_fields(definition)
+        if name in self._services:
+            service = self._services[name]
+            reply = service['reply']
+            if _is_stream(service):
+                return Dump(name, reply, crc, filter(_is_request_field, fields), definition)
+            if reply:
+                return Request(name, reply, crc, filter(_is_request_field, fields), definition)
+            else:
+                return Event(name, crc, filter(_is_request_field, fields), definition)
+        elif name in self._requests_by_reply:
+            return Reply(name, self._requests_by_reply[name], crc, filter(_is_reply_field, fields), definition)
+        elif name in self._dumps_by_details:
+            return Details(name, self._dumps_by_details[name], crc, filter(_is_reply_field, fields), definition)
+        else:
+            # TODO: some messages like combined_counters are not visible in the services.
+            # Throw exception instead (requires fixing vppagigen).
+            return Event(name, crc, filter(_is_request_field, fields), definition)
+
+    def _parse_fields(self, definition):
+        crc = None
+        fields = []
+        for item in definition:
+            if type(item) == dict and 'crc' in item:
+                crc = item['crc']
+            else:
+                fields.append(self._parse_field(item, fields))
+        if not crc:
+            raise ParseException("CRC was not defined for %s" % definition)
+        return crc, fields
+
+    def _parse_field(self, field, fields):
+        type_name = _extract_type_name(field[0])
+        if type_name in self._types_by_name:
+            if len(field) > 2:
+                # Array field
+                array_len_field = None
+                if len(field) == 4:
+                    for f in fields:
+                        if f.name == field[3]:
+                            array_len_field = f
+                    if not array_len_field:
+                        raise ParseException("Could not find field %s declared as length of array %s",
+                                             field[3], field[1])
+                return Field(field[1], self._types_by_name[type_name + _ARRAY_SUFFIX], field[2], array_len_field)
+            else:
+                return Field(field[1], self._types_by_name[type_name])
+        else:
+            raise ParseException("Unknown field type %s" % field)
+
+    def _validate_messages(self):
+        """
+        In case if message A is known to be reply for message B, and message B was not correctly parsed,
+        remove message A from the set of all messages.
+        """
+        to_be_removed = []
+        messages = self._messages_by_name
+        for name, msg in messages.iteritems():
+            if (is_request(msg) and msg.reply not in messages) \
+                    or (is_reply(msg) and msg.request not in messages) \
+                    or (is_dump(msg) and msg.details not in messages) \
+                    or (is_details(msg) and msg.dump not in messages):
+                to_be_removed.append(name)
+
+        for name in to_be_removed:
+            del messages[name]
+
+        self.messages = self._messages_by_name.values()
+
+_ARRAY_SUFFIX = '[]'
+
+
+def _underscore_to_camelcase_upper(name):
+    return name.title().replace("_", "")
+
+
+def _underscore_to_camelcase_lower(name):
+    name = name.title().replace("_", "")
+    return name[0].lower() + name[1:]
+
+
+def _message_to_javadoc(message_definition):
+    """ Converts JSON message definition to javadoc """
+    formatted_message = pprint.pformat(message_definition, indent=4, width=120, depth=None)
+    return " * " + formatted_message.replace("\n", "\n * ")
+
+
+def _is_stream(service):
+    """
+    Checks if service represents stream, e.g.:
+    "ip_address_dump": {
+        "reply": "ip_address_details",
+        "stream": true
+    }
+    :param service: JSON definition of service
+    :return: value assigned to "stream" or None
+    """
+    return "stream" in service
+
+
+def _extract_type_name(name):
+    if name.startswith(_VPP_TYPE_PREFIX) and name.endswith(_VPP_TYPE_SUFFIX):
+        return name[len(_VPP_TYPE_PREFIX): - len(_VPP_TYPE_SUFFIX)]
+    return name
+
+_VPP_TYPE_PREFIX = "vl_api_"
+
+_VPP_TYPE_SUFFIX = "_t"
+
+
+def _is_request_field(field):
+    # Skip fields that are hidden to the jvpp user (handled by JNI layer)
+    return field.name not in {'_vl_msg_id', 'client_index', 'context'}
+
+
+def _is_reply_field(field):
+    # Skip fields that are hidden to the jvpp user:
+    # _vl_msg_id is handled at JNI layer,
+    # Unlike in the request case, context is visible to allow matching replies with requests at Java layer.
+    return field.name not in {'_vl_msg_id'}
index 2008a42..69e870e 100644 (file)
@@ -1,6 +1,6 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2
 #
-# Copyright (c) 2016 Cisco and/or its affiliates.
+# Copyright (c) 2016,2018 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:
 # 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
 
-import os
+from jvpp_model import is_control_ping, is_control_ping_reply, is_dump, is_request
 
-import callback_gen
-import util
-from string import Template
 
-notification_registry_template = Template("""
-package $plugin_package.$notification_package;
+def generate_notifications(work_dir, model, logger):
+    """ Generates notification registry interface and implementation """
+    logger.debug("Generating Notification interfaces and implementation for %s" % model.json_api_files)
+    messages = filter(_notification_filter, model.messages)
+    _generate_global_event_callback(work_dir, model, messages)
+    _generate_event_registry(work_dir, model, messages)
+    _generate_event_registry_impl(work_dir, model, messages)
+    _generate_event_registry_provider(work_dir, model)
+
+
+def _notification_filter(msg):
+    # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client).
+    # Also skip control ping managed by jvpp registry.
+    return (not is_control_ping(msg)) and \
+           (not is_control_ping_reply(msg)) and \
+           (not is_dump(msg)) and \
+           (not is_request(msg))
+
+
+def _generate_event_registry(work_dir, model, messages):
+    plugin_name = model.plugin_java_name
+    plugin_package = model.plugin_package
+
+    register_callback_methods = []
+    for msg in messages:
+        name = _callback_name(msg)
+        fqn_name = _fqn_callback_name(plugin_package, name)
+        # TODO create NotificationListenerRegistration and return that instead of AutoCloseable to better indicate
+        # that the registration should be closed
+        register_callback_methods.append("    java.lang.AutoCloseable register%s(%s callback);" % (name, fqn_name))
+
+    with open("%s/%sEventRegistry.java" % (work_dir, plugin_name), "w") as f:
+        f.write(_EVENT_REGISTRY_TEMPLATE.substitute(
+            plugin_package=plugin_package,
+            plugin_name=plugin_name,
+            json_filename=model.json_api_files,
+            register_callback_methods="\n".join(register_callback_methods)
+        ))
+
+_EVENT_REGISTRY_TEMPLATE = Template("""
+package $plugin_package.notification;
 
 /**
  * <p>Registry for notification callbacks defined in ${plugin_name}.
- * <br>It was generated by notification_gen.py based on $inputfile
- * <br>(python representation of api file generated by vppapigen).
+ * <br>It was generated by notification_gen.py based on $json_filename.
  */
-public interface ${plugin_name}EventRegistry extends $base_package.$notification_package.EventRegistry {
+public interface ${plugin_name}EventRegistry extends io.fd.vpp.jvpp.notification.EventRegistry {
 
-    $register_callback_methods
+$register_callback_methods
 
     @Override
     void close();
 }
 """)
 
-global_notification_callback_template = Template("""
-package $plugin_package.$notification_package;
 
-/**
- * <p>Aggregated callback interface for notifications only.
- * <br>It was generated by notification_gen.py based on $inputfile
- * <br>(python representation of api file generated by vppapigen).
- */
-public interface Global${plugin_name}EventCallback$callbacks {
+def _generate_event_registry_impl(work_dir, model, messages):
+    plugin_name = model.plugin_java_name
+    plugin_package = model.plugin_package
 
-}
+    register_callback_methods = []
+    handler_methods = []
+    for msg in messages:
+        notification = msg.java_name_upper
+        callback = "%sCallback" % notification
+        register_callback_methods.append(_REGISTER_CALLBACK_IMPL_TEMPLATE.substitute(
+            plugin_package=plugin_package,
+            notification=notification,
+            callback=callback
+        ))
+        handler_methods.append(_HANDLER_IMPL_TEMPLATE.substitute(
+            plugin_package=plugin_package,
+            notification=notification,
+            callback=callback
+        ))
+
+    with open("%s/%sEventRegistryImpl.java" % (work_dir, plugin_name), "w") as f:
+        f.write(_EVENT_REGISTRY_IMPL_TEMPLATE.substitute(
+            plugin_package=plugin_package,
+            plugin_name=plugin_name,
+            json_filename=model.json_api_files,
+            register_callback_methods="".join(register_callback_methods),
+            handler_methods="".join(handler_methods)
+        ))
+
+_REGISTER_CALLBACK_IMPL_TEMPLATE = Template("""
+    public java.lang.AutoCloseable register$callback(final $plugin_package.callback.$callback callback){
+        if(null != registeredCallbacks.putIfAbsent($plugin_package.dto.$notification.class, callback)){
+            throw new IllegalArgumentException("Callback for " + $plugin_package.dto.$notification.class +
+                "notification already registered");
+        }
+        return () -> registeredCallbacks.remove($plugin_package.dto.$notification.class);
+    }
+""")
+
+_HANDLER_IMPL_TEMPLATE = Template("""
+    @Override
+    public void on$notification(
+        final $plugin_package.dto.$notification notification) {
+        if (LOG.isLoggable(java.util.logging.Level.FINE)) {
+            LOG.fine(String.format("Received $notification event message: %s", notification));
+        }
+        final io.fd.vpp.jvpp.callback.JVppCallback jVppCallback = registeredCallbacks.get($plugin_package.dto.$notification.class);
+        if (null != jVppCallback) {
+            (($plugin_package.callback.$callback) registeredCallbacks
+                .get($plugin_package.dto.$notification.class))
+                .on$notification(notification);
+        }
+    }
 """)
 
-notification_registry_impl_template = Template("""
-package $plugin_package.$notification_package;
+_EVENT_REGISTRY_IMPL_TEMPLATE = Template("""
+package $plugin_package.notification;
 
 /**
  * <p>Notification registry delegating notification processing to registered callbacks.
- * <br>It was generated by notification_gen.py based on $inputfile
- * <br>(python representation of api file generated by vppapigen).
+ * <br>It was generated by notification_gen.py based on $json_filename.
  */
 public final class ${plugin_name}EventRegistryImpl implements ${plugin_name}EventRegistry, Global${plugin_name}EventCallback {
 
     // TODO add a special NotificationCallback interface and only allow those to be registered
-    private final java.util.concurrent.ConcurrentMap<Class<?>, $base_package.$callback_package.JVppCallback> registeredCallbacks =
+    private final java.util.concurrent.ConcurrentMap<Class<?>, io.fd.vpp.jvpp.callback.JVppCallback> registeredCallbacks =
         new java.util.concurrent.ConcurrentHashMap<>();
     private static java.util.logging.Logger LOG = java.util.logging.Logger.getLogger(${plugin_name}EventRegistryImpl.class.getName());
 
@@ -81,41 +159,58 @@ public final class ${plugin_name}EventRegistryImpl implements ${plugin_name}Even
 }
 """)
 
-register_callback_impl_template = Template("""
-    public java.lang.AutoCloseable register$callback(final $plugin_package.$callback_package.$callback callback){
-        if(null != registeredCallbacks.putIfAbsent($plugin_package.$dto_package.$notification.class, callback)){
-            throw new IllegalArgumentException("Callback for " + $plugin_package.$dto_package.$notification.class +
-                "notification already registered");
-        }
-        return () -> registeredCallbacks.remove($plugin_package.$dto_package.$notification.class);
-    }
-""")
 
-handler_impl_template = Template("""
-    @Override
-    public void on$notification(
-        final $plugin_package.$dto_package.$notification_reply notification) {
-        if (LOG.isLoggable(java.util.logging.Level.FINE)) {
-            LOG.fine(String.format("Received $notification event message: %s", notification));
-        }
-        final $base_package.$callback_package.JVppCallback jVppCallback = registeredCallbacks.get($plugin_package.$dto_package.$notification.class);
-        if (null != jVppCallback) {
-            (($plugin_package.$callback_package.$callback) registeredCallbacks
-                .get($plugin_package.$dto_package.$notification.class))
-                .on$notification(notification);
-        }
-    }
+def _generate_global_event_callback(work_dir, model, messages):
+    plugin_name = model.plugin_java_name
+    plugin_package = model.plugin_package
+
+    callbacks = ""
+    callback_list = []
+    for msg in messages:
+        fqn_name = _fqn_callback_name(plugin_package, _callback_name(msg))
+        callback_list.append(fqn_name)
+
+    if callback_list:
+        callbacks = " extends %s" % ", ".join(callback_list)
+
+    with open("%s/Global%sEventCallback.java" % (work_dir, plugin_name), "w") as f:
+        f.write(_GLOBAL_EVENT_CALLBACK_TEMPLATE.substitute(
+            plugin_package=plugin_package,
+            plugin_name=plugin_name,
+            json_filename=model.json_api_files,
+            callbacks=callbacks
+        ))
+
+_GLOBAL_EVENT_CALLBACK_TEMPLATE = Template("""
+package $plugin_package.notification;
+
+/**
+ * <p>Aggregated callback interface for notifications only.
+ * <br>It was generated by notification_gen.py based on $json_filename.
+ */
+public interface Global${plugin_name}EventCallback$callbacks {
+
+}
 """)
 
-notification_provider_template = Template("""
-package $plugin_package.$notification_package;
+
+def _generate_event_registry_provider(work_dir, model):
+    plugin_name = model.plugin_java_name
+    with open("%s/%sEventRegistryProvider.java" % (work_dir, plugin_name), "w") as f:
+        f.write(_EVENT_REGISTRY_PROVIDER_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            plugin_name=plugin_name,
+            json_filename=model.json_api_files
+        ))
+
+_EVENT_REGISTRY_PROVIDER_TEMPLATE = Template("""
+package $plugin_package.notification;
 
  /**
  * Provides ${plugin_name}EventRegistry.
- * <br>The file was generated by notification_gen.py based on $inputfile
- * <br>(python representation of api file generated by vppapigen).
+ * <br>The file was generated by notification_gen.py based on $json_filename.
  */
-public interface ${plugin_name}EventRegistryProvider extends $base_package.$notification_package.EventRegistryProvider {
+public interface ${plugin_name}EventRegistryProvider extends io.fd.vpp.jvpp.notification.EventRegistryProvider {
 
     @Override
     public ${plugin_name}EventRegistry getEventRegistry();
@@ -123,93 +218,9 @@ public interface ${plugin_name}EventRegistryProvider extends $base_package.$noti
 """)
 
 
-def generate_notification_registry(func_list, base_package, plugin_package, plugin_name, notification_package,
-                                   callback_package, dto_package, inputfile, logger):
-    """ Generates notification registry interface and implementation """
-    logger.debug("Generating Notification interfaces and implementation for %s" % inputfile)
+def _callback_name(msg):
+    return "%sCallback" % msg.java_name_upper
 
-    if not os.path.exists(notification_package):
-        os.mkdir(notification_package)
-
-    callbacks = []
-    register_callback_methods = []
-    register_callback_methods_impl = []
-    handler_methods = []
-    for func in func_list:
-        camel_case_name_with_suffix = util.underscore_to_camelcase_upper(func['name'])
-
-        if util.is_control_ping(camel_case_name_with_suffix):
-            # Skip control ping managed by jvpp registry.
-            continue
-        if util.is_dump(func['name']) or util.is_request(func['name'], func_list):
-            continue
-
-        # Generate callbacks for all messages except for dumps and requests (handled by vpp, not client).
-        notification_dto = camel_case_name_with_suffix
-        callback_ifc = camel_case_name_with_suffix + callback_gen.callback_suffix
-        fully_qualified_callback_ifc = "{0}.{1}.{2}".format(plugin_package, callback_package, callback_ifc)
-        callbacks.append(fully_qualified_callback_ifc)
-
-        # TODO create NotificationListenerRegistration and return that instead of AutoCloseable to better indicate
-        # that the registration should be closed
-        register_callback_methods.append("java.lang.AutoCloseable register{0}({1} callback);"
-                                         .format(callback_ifc, fully_qualified_callback_ifc))
-        register_callback_methods_impl.append(register_callback_impl_template.substitute(plugin_package=plugin_package,
-                                                                                         callback_package=callback_package,
-                                                                                         dto_package=dto_package,
-                                                                                         notification=camel_case_name_with_suffix,
-                                                                                         callback=callback_ifc))
-        handler_methods.append(handler_impl_template.substitute(base_package=base_package,
-                                                                plugin_package=plugin_package,
-                                                                callback_package=callback_package,
-                                                                dto_package=dto_package,
-                                                                notification=notification_dto,
-                                                                notification_reply=camel_case_name_with_suffix,
-                                                                callback=callback_ifc))
-
-    callback_file = open(os.path.join(notification_package, "%sEventRegistry.java" % plugin_name), 'w')
-    callback_file.write(notification_registry_template.substitute(inputfile=inputfile,
-                                                                register_callback_methods="\n    ".join(register_callback_methods),
-                                                                base_package=base_package,
-                                                                plugin_package=plugin_package,
-                                                                plugin_name=plugin_name,
-                                                                notification_package=notification_package))
-    callback_file.flush()
-    callback_file.close()
-
-    callback_file = open(os.path.join(notification_package, "Global%sEventCallback.java" % plugin_name), 'w')
-
-    global_notification_callback_callbacks = ""
-    if callbacks:
-        global_notification_callback_callbacks = " extends " + ", ".join(callbacks)
-
-    callback_file.write(global_notification_callback_template.substitute(inputfile=inputfile,
-                                                                       callbacks=global_notification_callback_callbacks,
-                                                                       plugin_package=plugin_package,
-                                                                       plugin_name=plugin_name,
-                                                                       notification_package=notification_package))
-    callback_file.flush()
-    callback_file.close()
-
-    callback_file = open(os.path.join(notification_package, "%sEventRegistryImpl.java" % plugin_name), 'w')
-    callback_file.write(notification_registry_impl_template.substitute(inputfile=inputfile,
-                                                                     callback_package=callback_package,
-                                                                     dto_package=dto_package,
-                                                                     register_callback_methods="".join(register_callback_methods_impl),
-                                                                     handler_methods="".join(handler_methods),
-                                                                     base_package=base_package,
-                                                                     plugin_package=plugin_package,
-                                                                     plugin_name=plugin_name,
-                                                                     notification_package=notification_package))
-    callback_file.flush()
-    callback_file.close()
-
-    callback_file = open(os.path.join(notification_package, "%sEventRegistryProvider.java" % plugin_name), 'w')
-    callback_file.write(notification_provider_template.substitute(inputfile=inputfile,
-                                                                     base_package=base_package,
-                                                                     plugin_package=plugin_package,
-                                                                     plugin_name=plugin_name,
-                                                                     notification_package=notification_package))
-    callback_file.flush()
-    callback_file.close()
 
+def _fqn_callback_name(plugin_package, callback_name):
+    return "%s.callback.%s" % (plugin_package, callback_name)
old mode 100644 (file)
new mode 100755 (executable)
index c655933..3767b53
@@ -1,6 +1,6 @@
-#!/usr/bin/env python
+#!/usr/bin/env python2
 #
-# Copyright (c) 2016 Cisco and/or its affiliates.
+# Copyright (c) 2016,2018 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:
 # 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.
-
-import os
 from string import Template
 
-import util
-import jni_gen
-import dto_gen
+from jvpp_common_gen import generate_hash_code, generate_equals, generate_to_string, generate_fields
+from jvpp_model import Class
+
+
+def generate_types(work_dir, model, logger):
+    logger.debug("Generating custom types for %s " % model.json_api_files)
+
+    for t in model.types:
+        if not isinstance(t, Class):
+            continue
+        logger.debug("Generating DTO for type %s", t)
+        type_class_name = t.java_name
+        fields = t.fields
+        type_class = _TYPE_TEMPLATE.substitute(
+            plugin_package=model.plugin_package,
+            c_type_name=t.name,
+            json_filename=model.json_api_files,
+            json_definition=t.doc,
+            java_type_name=type_class_name,
+            fields=generate_fields(fields),
+            hash_code=generate_hash_code(fields),
+            equals=generate_equals(type_class_name, fields),
+            to_string=generate_to_string(type_class_name, fields)
+        )
+        with open("%s/%s.java" % (work_dir, type_class_name), "w") as f:
+            f.write(type_class)
 
-type_template = Template("""
-package $plugin_package.$type_package;
+_TYPE_TEMPLATE = Template("""
+package $plugin_package.types;
 
 /**
  * <p>This class represents $c_type_name type definition.
- * <br>It was generated by types_gen.py based on $inputfile preparsed data:
+ * <br>It was generated by jvpp_types_gen.py based on $json_filename:
  * <pre>
-$docs
+$json_definition
  * </pre>
  */
 public final class $java_type_name {
 $fields
-$methods
+$hash_code
+$equals
+$to_string
 }
 """)
-
-field_template = Template("""    public $type $name;\n""")
-
-
-def generate_type_fields(type_definition):
-    """
-    Generates fields for class representing typeonly definition
-    :param type_definition: python representation of typeonly definition
-    :return: string representing class fields
-    """
-    fields = ""
-    for t in zip(type_definition['types'], type_definition['args']):
-        field_name = util.underscore_to_camelcase(t[1])
-        fields += field_template.substitute(type=util.jni_2_java_type_mapping[t[0]],
-                                            name=field_name)
-    return fields
-
-object_struct_setter_template = Template("""
-    {
-        jclass ${field_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
-        memset (&(mp->${c_name}), 0, sizeof (mp->${c_name}));
-        ${struct_initialization}
-    }
-""")
-
-object_array_struct_setter_template = Template("""
-    {
-        jclass ${field_reference_name}ArrayElementClass = (*env)->FindClass(env, "${class_FQN}");
-        if (${field_reference_name}) {
-            size_t _i;
-            jsize cnt = (*env)->GetArrayLength (env, ${field_reference_name});
-            ${field_length_check}
-            for (_i = 0; _i < cnt; _i++) {
-                jobject ${field_reference_name}ArrayElement = (*env)->GetObjectArrayElement(env, ${field_reference_name}, _i);
-                memset (&(mp->${c_name}[_i]), 0, sizeof (mp->${c_name}[_i]));
-                ${struct_initialization}
-            }
-        }
-    }
-""")
-
-object_dto_field_setter_template = Template("""
-    {
-        jclass ${field_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
-        jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "<init>", "()V");
-        jobject ${field_reference_name} = (*env)->NewObject(env, ${field_reference_name}Class,  ${field_reference_name}Constructor);
-        ${type_initialization}
-        (*env)->SetObjectField(env, dto, ${field_reference_name}FieldId, ${field_reference_name});
-        (*env)->DeleteLocalRef(env, ${field_reference_name});
-    }
-""")
-
-object_array_dto_field_setter_template = Template("""
-    {
-        jclass ${field_reference_name}Class = (*env)->FindClass(env, "${class_FQN}");
-        jobjectArray ${field_reference_name} = (*env)->NewObjectArray(env, ${field_length}, ${field_reference_name}Class, 0);
-        jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "<init>", "()V");
-        unsigned int _i;
-        for (_i = 0; _i < ${field_length}; _i++) {
-            jobject ${field_reference_name}ArrayElement = (*env)->NewObject(env, ${field_reference_name}Class,  ${field_reference_name}Constructor);
-            ${type_initialization}
-            (*env)->SetObjectArrayElement(env, ${field_reference_name}, _i, ${field_reference_name}ArrayElement);
-            (*env)->DeleteLocalRef(env, ${field_reference_name}ArrayElement);
-        }
-        (*env)->SetObjectField(env, dto, ${field_reference_name}FieldId, ${field_reference_name});
-        (*env)->DeleteLocalRef(env, ${field_reference_name});
-    }
-""")
-
-
-def generate_struct_initialization(type_def, c_name_prefix, object_name, indent):
-    struct_initialization = ""
-    # field identifiers
-    for t in zip(type_def['types'], type_def['args'], type_def['lengths']):
-        field_reference_name = "${c_name}" + util.underscore_to_camelcase_upper(t[1])
-        field_name = util.underscore_to_camelcase(t[1])
-        struct_initialization += jni_gen.jni_request_identifiers_for_type(field_type=t[0],
-                                                                          field_reference_name=field_reference_name,
-                                                                          field_name=field_name,
-                                                                          object_name=object_name)
-        struct_initialization += jni_gen.jni_request_binding_for_type(field_type=t[0], c_name=c_name_prefix + t[1],
-                                                                      field_reference_name=field_reference_name,
-                                                                      field_length=t[2][0],
-                                                                      is_variable_len_array=t[2][1])
-    return indent + struct_initialization.replace('\n', '\n' + indent)
-
-
-def generate_type_setter(handler_name, type_def, c_name_prefix, object_name, indent):
-    type_initialization = ""
-    for t in zip(type_def['types'], type_def['args'], type_def['lengths']):
-        field_length = t[2][0]
-        is_variable_len_array = t[2][1]
-        length_field_type = None
-        if is_variable_len_array:
-            length_field_type = type_def['types'][type_def['args'].index(field_length)]
-        type_initialization += jni_gen.jni_reply_handler_for_type(handler_name=handler_name,
-                                                                  ref_name="${field_reference_name}",
-                                                                  field_type=t[0], c_name=c_name_prefix + t[1],
-                                                                  field_reference_name="${c_name}" + util.underscore_to_camelcase_upper(t[1]),
-                                                                  field_name=util.underscore_to_camelcase(t[1]),
-                                                                  field_length=field_length,
-                                                                  is_variable_len_array=is_variable_len_array,
-                                                                  length_field_type=length_field_type,
-                                                                  object_name=object_name)
-    return indent + type_initialization.replace('\n', '\n' + indent)
-
-
-def generate_types(types_list, plugin_package, types_package, inputfile, logger):
-    """
-    Generates Java representation of custom types defined in api file.
-    """
-
-    if not types_list:
-        logger.debug("Skipping custom types generation (%s does not define custom types)." % inputfile)
-        return
-
-    logger.debug("Generating custom types for %s" % inputfile)
-
-    if not os.path.exists(types_package):
-        os.mkdir(types_package)
-
-    for type in types_list:
-        c_type_name = type['name']
-        java_type_name = util.underscore_to_camelcase_upper(type['name'])
-        dto_path = os.path.join(types_package, java_type_name + ".java")
-
-        fields = generate_type_fields(type)
-
-        dto_file = open(dto_path, 'w')
-        dto_file.write(type_template.substitute(plugin_package=plugin_package,
-                                                type_package=types_package,
-                                                c_type_name=c_type_name,
-                                                inputfile=inputfile,
-                                                docs=util.api_message_to_javadoc(type),
-                                                java_type_name=java_type_name,
-                                                fields=fields,
-                                                methods=dto_gen.generate_dto_base_methods(java_type_name, type)
-                                                ))
-
-        # update type mappings:
-        # todo fix vpe.api to use type_name instead of vl_api_type_name_t
-        type_name = "vl_api_" + c_type_name + "_t"
-        java_fqn = "%s.%s.%s" % (plugin_package, types_package, java_type_name)
-        util.vpp_2_jni_type_mapping[type_name] = "jobject"
-        util.vpp_2_jni_type_mapping[type_name + "[]"] = "jobjectArray"
-        util.jni_2_java_type_mapping[type_name] = java_fqn
-        util.jni_2_java_type_mapping[type_name + "[]"] = java_fqn + "[]"
-        jni_name = java_fqn.replace('.', "/")
-        jni_signature = "L" + jni_name + ";"
-        util.jni_2_signature_mapping[type_name] = "L" + jni_name + ";"
-        util.jni_2_signature_mapping[type_name + "[]"] = "[" + jni_signature
-        util.jni_field_accessors[type_name] = "ObjectField"
-        util.jni_field_accessors[type_name + "[]"] = "ObjectField"
-
-        jni_gen.struct_setter_templates[type_name] = Template(
-                object_struct_setter_template.substitute(
-                        c_name="${c_name}",
-                        field_reference_name="${field_reference_name}",
-                        class_FQN=jni_name,
-                        struct_initialization=generate_struct_initialization(type, "${c_name}.",
-                                                                           "${field_reference_name}", ' ' * 4))
-        )
-
-        jni_gen.struct_setter_templates[type_name+ "[]"] = Template(
-                object_array_struct_setter_template.substitute(
-                        c_name="${c_name}",
-                        field_reference_name="${field_reference_name}",
-                        field_length_check="${field_length_check}",
-                        class_FQN=jni_name,
-                        struct_initialization=generate_struct_initialization(type, "${c_name}[_i].",
-                                                                           "${field_reference_name}ArrayElement", ' ' * 8))
-        )
-
-        jni_gen.dto_field_setter_templates[type_name] = Template(
-                object_dto_field_setter_template.substitute(
-                        field_reference_name="${field_reference_name}",
-                        field_length="${field_length}",
-                        class_FQN=jni_name,
-                        type_initialization=generate_type_setter(c_type_name, type, "${c_name}.",
-                                                                 "${field_reference_name}", ' ' * 4))
-        )
-
-        jni_gen.dto_field_setter_templates[type_name + "[]"] = Template(
-                object_array_dto_field_setter_template.substitute(
-                        field_reference_name="${field_reference_name}",
-                        field_length="${field_length}",
-                        class_FQN=jni_name,
-                        type_initialization=generate_type_setter(c_type_name, type, "${c_name}[_i].",
-                                                                 "${field_reference_name}ArrayElement", ' ' * 8))
-        )
-
-        dto_file.flush()
-        dto_file.close()
-
diff --git a/src/vpp-api/java/jvpp/gen/jvppgen/util.py b/src/vpp-api/java/jvpp/gen/jvppgen/util.py
deleted file mode 100644 (file)
index 70ddc8e..0000000
+++ /dev/null
@@ -1,189 +0,0 @@
-#!/usr/bin/env python
-#
-# Copyright (c) 2016 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.
-
-import os, pprint
-from os import removedirs
-
-
-def underscore_to_camelcase(name):
-    name = name.title().replace("_", "")
-    return name[0].lower() + name[1:]
-
-
-def underscore_to_camelcase_upper(name):
-    name = name.title().replace("_", "")
-    return name[0].upper() + name[1:]
-
-
-def remove_folder(folder):
-    """ Remove folder with all its files """
-    for root, dirs, files in os.walk(folder, topdown=False):
-        for name in files:
-            os.remove(os.path.join(root, name))
-        removedirs(folder)
-
-
-_REPLY_SUFFIX = "reply"
-_DETAILS_SUFFIX = "details"
-_REPLY_SUFFIXES = (_REPLY_SUFFIX, _DETAILS_SUFFIX)
-
-
-def is_reply(name):
-    return name.lower().endswith(_REPLY_SUFFIXES)
-
-
-def is_request(msg_name_underscore, all_messages):
-    """
-    Checks if reply message is present in all_messages.
-
-    :param msg_name_underscore name of vpp API message
-    :param all_messages: sequence of vpp message names
-    :returns: True if reply for the msg_name_underscore message is defined.
-    """
-    reply_msg = msg_name_underscore + "_" + _REPLY_SUFFIX
-    return reply_msg in [m['name'] for m in all_messages]
-
-
-def is_details(name):
-    return name.lower().endswith(_DETAILS_SUFFIX)
-
-
-dump_suffix = "dump"
-
-
-def is_dump(name):
-    return name.lower().endswith(dump_suffix)
-
-
-def is_retval_field(name):
-    return name == 'retval'
-
-
-def get_reply_suffix(name):
-    for reply_suffix in _REPLY_SUFFIXES:
-        if name.lower().endswith(reply_suffix):
-            return reply_suffix
-
-# Mapping according to:
-# http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html
-#
-# Unsigned types are converted to signed java types that have the same size.
-# It is the API user responsibility to interpret them correctly.
-jni_2_java_type_mapping = {'u8': 'byte',
-                           'u8[]': 'byte[]',
-                           'i8': 'byte',
-                           'i8[]': 'byte[]',
-                           'u16': 'short',
-                           'u16[]': 'short[]',
-                           'i16': 'short',
-                           'i16[]': 'short[]',
-                           'u32': 'int',
-                           'u32[]': 'int[]',
-                           'i32': 'int',
-                           'i32[]': 'int[]',
-                           'u64': 'long',
-                           'u64[]': 'long[]',
-                           'i64': 'long',
-                           'i64[]': 'long[]',
-                           'f64': 'double',
-                           'f64[]': 'double[]'
-                           }
-
-vpp_2_jni_type_mapping = {'u8': 'jbyte',
-                          'u8[]': 'jbyteArray',
-                          'i8': 'jbyte',
-                          'i8[]': 'jbyteArray',
-                          'u16': 'jshort',
-                          'u16[]': 'jshortArray',
-                          'i16': 'jshort',
-                          'i16[]': 'jshortArray',
-                          'u32': 'jint',
-                          'u32[]': 'jintArray',
-                          'i32': 'jint',
-                          'i32[]': 'jintArray',
-                          'u64': 'jlong',
-                          'u64[]': 'jlongArray',
-                          'i64': 'jlong',
-                          'i64[]': 'jlongArray',
-                          'f64': 'jdouble',
-                          'f64[]': 'jdoubleArray'
-                          }
-
-# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#type_signatures
-jni_2_signature_mapping = {'u8': 'B',
-                           'u8[]': '[B',
-                           'i8': 'B',
-                           'i8[]': '[B',
-                           'u16': 'S',
-                           'u16[]': '[S',
-                           'i16': 'S',
-                           'i16[]': '[S',
-                           'u32': 'I',
-                           'u32[]': '[I',
-                           'i32': 'I',
-                           'i32[]': '[I',
-                           'u64': 'J',
-                           'u64[]': '[J',
-                           'i64': 'J',
-                           'i64[]': '[J',
-                           'f64': 'D',
-                           'f64[]': '[D'
-                           }
-
-# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#Get_type_Field_routines
-jni_field_accessors =  {'u8': 'ByteField',
-                        'u8[]': 'ObjectField',
-                        'i8': 'ByteField',
-                        'i8[]': 'ObjectField',
-                        'u16': 'ShortField',
-                        'u16[]': 'ObjectField',
-                        'i16': 'ShortField',
-                        'i16[]': 'ObjectField',
-                        'u32': 'IntField',
-                        'u32[]': 'ObjectField',
-                        'i32': 'IntField',
-                        'i32[]': 'ObjectField',
-                        'u64': 'LongField',
-                        'u64[]': 'ObjectField',
-                        'i64': 'LongField',
-                        'i64[]': 'ObjectField',
-                        'f64': 'DoubleField',
-                        'f64[]': 'ObjectField'
-                        }
-
-
-def remove_reply_suffix(camel_case_name_with_suffix):
-    return remove_suffix(camel_case_name_with_suffix, get_reply_suffix(camel_case_name_with_suffix))
-
-
-def remove_suffix(camel_case_name_with_suffix, suffix):
-    if not suffix:
-        return camel_case_name_with_suffix
-    suffix_length = len(suffix)
-    return camel_case_name_with_suffix[:-suffix_length] if suffix_length != 0 else camel_case_name_with_suffix
-
-
-def is_control_ping(camel_case_name_with_suffix):
-    return camel_case_name_with_suffix.lower().startswith("controlping")
-
-
-def api_message_to_javadoc(api_message):
-    """ Converts vpe.api message description to javadoc """
-    str = pprint.pformat(api_message, indent=4, width=120, depth=None)
-    return " * " + str.replace("\n", "\n * ")
-
-
-def is_array(java_type_as_string):
-    return java_type_as_string.endswith("[]")