VPP-120: add custom types support to jvpp 99/3199/11
authorMarek Gradzki <mgradzki@cisco.com>
Thu, 29 Sep 2016 11:22:35 +0000 (13:22 +0200)
committerDamjan Marion <dmarion.lists@gmail.com>
Mon, 31 Oct 2016 21:42:40 +0000 (21:42 +0000)
Generates java classes based on typeonly definitions
(hashcode, equals and toString methods are also included).

Adds JNI handling for request and reply messages
(also arrays of custom types).

Change-Id: I16f1cea17899704426aa083fad1cb800a8d115df
Signed-off-by: Marek Gradzki <mgradzki@cisco.com>
vpp-api/java/Makefile.am
vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java [new file with mode: 0644]
vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/Readme.txt
vpp-api/java/jvpp-core/jvpp_core.c
vpp-api/java/jvpp/gen/jvpp_gen.py
vpp-api/java/jvpp/gen/jvppgen/dto_gen.py
vpp-api/java/jvpp/gen/jvppgen/jni_gen.py [new file with mode: 0644]
vpp-api/java/jvpp/gen/jvppgen/jvpp_c_gen.py
vpp-api/java/jvpp/gen/jvppgen/types_gen.py [new file with mode: 0644]
vpp-api/java/jvpp/gen/jvppgen/util.py

index cdb8379..390562b 100644 (file)
@@ -92,11 +92,13 @@ jvpp-core/io_fd_vpp_jvpp_core_JVppCoreImpl.h: jvpp-registry/io_fd_vpp_jvpp_VppJN
        cp -rf @srcdir@/jvpp-core/* -t jvpp-core/
        mkdir -p jvpp-core/target
        cd jvpp-core \
-               && mkdir -p dto future callfacade callback notification \
+               && mkdir -p types dto future callfacade callback notification \
                && @srcdir@/jvpp/gen/jvpp_gen.py -i defs_vpp_papi.py --plugin_name core \
-               && cp -rf dto future callfacade callback notification *.java -t $(packagedir_jvpp_core) \
-               && rm -rf dto future callfacade callback notification *.java
+               && cp -rf types dto future callfacade callback notification *.java -t $(packagedir_jvpp_core) \
+               && rm -rf types dto future callfacade callback notification *.java
+
        $(JAVAC) -classpath jvpp-registry/target -d jvpp-core/target jvpp-core/$(packagedir_jvpp_core)/*.java \
+               jvpp-core/$(packagedir_jvpp_core)/types/*.java \
                jvpp-core/$(packagedir_jvpp_core)/dto/*.java \
                jvpp-core/$(packagedir_jvpp_core)/callback/*.java \
                jvpp-core/$(packagedir_jvpp_core)/notification/*.java \
diff --git a/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java b/vpp-api/java/jvpp-core/io/fd/vpp/jvpp/core/test/LispAdjacencyTest.java
new file mode 100644 (file)
index 0000000..d7f5039
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * 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.
+ */
+
+package io.fd.vpp.jvpp.core.test;
+
+import io.fd.vpp.jvpp.JVppRegistry;
+import io.fd.vpp.jvpp.JVppRegistryImpl;
+import io.fd.vpp.jvpp.core.JVppCoreImpl;
+import io.fd.vpp.jvpp.core.dto.LispAddDelAdjacency;
+import io.fd.vpp.jvpp.core.dto.LispAddDelLocalEid;
+import io.fd.vpp.jvpp.core.dto.LispAddDelLocatorSet;
+import io.fd.vpp.jvpp.core.dto.LispAddDelRemoteMapping;
+import io.fd.vpp.jvpp.core.dto.LispAdjacenciesGet;
+import io.fd.vpp.jvpp.core.dto.LispAdjacenciesGetReply;
+import io.fd.vpp.jvpp.core.dto.LispEnableDisable;
+import io.fd.vpp.jvpp.core.future.FutureJVppCoreFacade;
+import java.nio.charset.StandardCharsets;
+import java.util.concurrent.ExecutionException;
+import java.util.logging.Logger;
+
+/**
+ * Tests lisp adjacency creation and read (custom vpe.api type support showcase).
+ */
+public class LispAdjacencyTest {
+
+    private static final Logger LOG = Logger.getLogger(LispAdjacencyTest.class.getName());
+
+    private static void enableLisp(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException {
+        final LispEnableDisable request = new LispEnableDisable();
+        request.isEn = 1;
+        jvpp.lispEnableDisable(request).toCompletableFuture().get();
+        LOG.info("Lisp enabled successfully");
+    }
+
+    private static void addLocatorSet(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException {
+        final LispAddDelLocatorSet request = new LispAddDelLocatorSet();
+        request.isAdd = 1;
+        request.locatorSetName = "ls1".getBytes(StandardCharsets.UTF_8);
+        jvpp.lispAddDelLocatorSet(request).toCompletableFuture().get();
+        LOG.info("Locator set created successfully:" + request.toString());
+    }
+
+    private static void addLocalEid(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException {
+        final LispAddDelLocalEid request = new LispAddDelLocalEid();
+        request.isAdd = 1;
+        request.locatorSetName = "ls1".getBytes(StandardCharsets.UTF_8);
+        request.eid = new byte[] {1, 2, 1, 10};
+        request.eidType = 0; // ip4
+        request.vni = 0;
+        request.prefixLen = 32;
+        jvpp.lispAddDelLocalEid(request).toCompletableFuture().get();
+        LOG.info("Local EID created successfully:" + request.toString());
+    }
+
+    private static void addRemoteMapping(final FutureJVppCoreFacade jvpp)
+        throws ExecutionException, InterruptedException {
+        final LispAddDelRemoteMapping request = new LispAddDelRemoteMapping();
+        request.isAdd = 1;
+        request.vni = 0;
+        request.eid = new byte[] {1, 2, 1, 20};
+        request.eidLen = 32;
+        request.rlocNum = 1;
+        request.rlocs = new byte[] {1, 1, 1, 1, 2, 1, 20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+        jvpp.lispAddDelRemoteMapping(request).toCompletableFuture().get();
+        LOG.info("Remote mapping created successfully:" + request.toString());
+    }
+
+    private static void addAdjacency(final FutureJVppCoreFacade jvpp) throws ExecutionException, InterruptedException {
+        final LispAddDelAdjacency request = new LispAddDelAdjacency();
+        request.isAdd = 1;
+        request.leid = new byte[] {1, 2, 1, 10};
+        request.leidLen = 32;
+        request.reid = new byte[] {1, 2, 1, 20};
+        request.reidLen = 32;
+        request.eidType = 0; // ip4
+        request.vni = 0;
+        jvpp.lispAddDelAdjacency(request).toCompletableFuture().get();
+        LOG.info("Lisp adjacency created successfully:" + request.toString());
+    }
+
+    private static void showAdjacencies(final FutureJVppCoreFacade jvpp)
+        throws ExecutionException, InterruptedException {
+        final LispAdjacenciesGetReply reply =
+            jvpp.lispAdjacenciesGet(new LispAdjacenciesGet()).toCompletableFuture().get();
+        LOG.info("Lisp adjacency received successfully:" + reply.toString());
+    }
+
+    private static void testAdjacency(final FutureJVppCoreFacade jvpp) throws Exception {
+        enableLisp(jvpp);
+        addLocatorSet(jvpp);
+        addLocalEid(jvpp);
+        addRemoteMapping(jvpp);
+        addAdjacency(jvpp);
+        showAdjacencies(jvpp);
+    }
+
+    private static void testFutureApi() throws Exception {
+        LOG.info("Create lisp adjacency test");
+        try (final JVppRegistry registry = new JVppRegistryImpl("LispAdjacencyTest");
+             final FutureJVppCoreFacade jvppFacade = new FutureJVppCoreFacade(registry, new JVppCoreImpl())) {
+            LOG.info("Successfully connected to VPP");
+
+            testAdjacency(jvppFacade);
+            LOG.info("Disconnecting...");
+        }
+    }
+
+    public static void main(String[] args) throws Exception {
+        testFutureApi();
+    }
+}
index 2c9c424..c1b0c5a 100644 (file)
@@ -14,3 +14,4 @@ CreateSubInterfaceTest - Tests sub-interface creation
 FutureApiNotificationTest - Tests interface notifications using Future based JVpp facade
 FutureApiTest - Execution of more complex calls using Future based JVpp facade
 L2AclTest - Tests L2 ACL creation
+LispAdjacencyTest - Tests lisp adjacency creation and read (custom vpe.api type support showcase)
index 8872ef5..83c0bb2 100644 (file)
@@ -71,8 +71,8 @@ JNIEXPORT void JNICALL Java_io_fd_vpp_jvpp_core_JVppCoreImpl_init0
         vl_msg_api_set_handlers(VL_API_##N, #n,     \
                 vl_api_##n##_t_handler,             \
                 vl_noop_handler,                    \
-                vl_api_##n##_t_endian,              \
-                vl_api_##n##_t_print,               \
+                vl_noop_handler,              \
+                vl_noop_handler,               \
                 sizeof(vl_api_##n##_t), 1);
         foreach_api_reply_handler;
     #undef _
index 80bb4b9..3be8382 100755 (executable)
@@ -19,6 +19,7 @@ import importlib
 import sys
 import os
 
+from jvppgen import types_gen
 from jvppgen import callback_gen
 from jvppgen import notification_gen
 from jvppgen import dto_gen
@@ -37,7 +38,6 @@ from jvppgen import util
 #
 # where
 # defs_api_vpp_papi.py - vpe.api in python format (generated by vppapigen)
-from jvppgen.util import vpp_2_jni_type_mapping
 
 parser = argparse.ArgumentParser(description='VPP Java API generator')
 parser.add_argument('-i', action="store", dest="inputfile")
@@ -47,7 +47,7 @@ args = parser.parse_args()
 
 sys.path.append(".")
 
-print "args.inputfile %s" % args.inputfile
+print "Generating Java API for %s" % args.inputfile
 importdir = os.path.dirname(args.inputfile)
 print "importdir %s" % importdir
 inputfile = os.path.basename(args.inputfile)
@@ -60,13 +60,6 @@ print "control_ping_class %s" % control_ping_class
 sys.path.append(importdir)
 cfg = importlib.import_module(inputfile, package=None)
 
-
-# FIXME: functions unsupported due to problems with vpe.api
-def is_supported(f_name):
-    return f_name not in {'vnet_ip4_fib_counters', 'vnet_ip6_fib_counters',
-            'lisp_adjacencies_get_reply', 'lisp_adjacencies_get'}
-
-
 def is_request_field(field_name):
     return field_name not in {'_vl_msg_id', 'client_index', 'context'}
 
@@ -86,60 +79,52 @@ def get_args(t, filter):
 
 def get_types(t, filter):
     types_list = []
-    c_types_list = []
     lengths_list = []
     for i in t:
         if not filter(i[1]):
             continue
         if len(i) is 3:  # array type
-            types_list.append(vpp_2_jni_type_mapping[i[0]] + 'Array')
-            c_types_list.append(i[0] + '[]')
+            types_list.append(i[0] + '[]')
             lengths_list.append((i[2], False))
         elif len(i) is 4:  # variable length array type
-            types_list.append(vpp_2_jni_type_mapping[i[0]] + 'Array')
-            c_types_list.append(i[0] + '[]')
+            types_list.append(i[0] + '[]')
             lengths_list.append((i[3], True))
         else:  # primitive type
-            types_list.append(vpp_2_jni_type_mapping[i[0]])
-            c_types_list.append(i[0])
+            types_list.append(i[0])
             lengths_list.append((0, False))
-    return types_list, c_types_list, lengths_list
+    return types_list, lengths_list
 
 
-def get_definitions():
+def get_definitions(defs):
     # Pass 1
     func_list = []
     func_name = {}
-    for a in cfg.messages:
-        if not is_supported(a[0]):
-            continue
-
+    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, c_types, lengths = get_types(a[1:], is_response_field)
+            types, lengths = get_types(a[1:], is_response_field)
             func_name[a[0]] = dict(
                 [('name', a[0]), ('java_name', java_name),
                  ('args', get_args(a[1:], is_response_field)), ('full_args', get_args(a[1:], lambda x: True)),
-                 ('types', types), ('c_types', c_types), ('lengths', lengths)])
+                 ('types', types), ('lengths', lengths)])
         # For requests skip message_id, client_id and context
         else:
-            types, c_types, lengths = get_types(a[1:], is_request_field)
+            types, lengths = get_types(a[1:], is_request_field)
             func_name[a[0]] = dict(
                 [('name', a[0]), ('java_name', java_name),
                  ('args', get_args(a[1:], is_request_field)), ('full_args', get_args(a[1:], lambda x: True)),
-                 ('types', types), ('c_types', c_types), ('lengths', lengths)])
+                 ('types', types), ('lengths', lengths)])
 
         # Indexed by name
         func_list.append(func_name[a[0]])
     return func_list, func_name
 
 
-func_list, func_name = get_definitions()
-
 base_package = 'io.fd.vpp.jvpp'
 plugin_package = base_package + '.' + plugin_name
+types_package = 'types'
 dto_package = 'dto'
 callback_package = 'callback'
 notification_package = 'notification'
@@ -148,6 +133,11 @@ future_package = 'future'
 callback_facade_package = 'callfacade'
 control_ping_class_fqn = "%s.%s.%s" % (plugin_package, dto_package, control_ping_class)
 
+types_list, types_name = get_definitions(cfg.types)
+
+types_gen.generate_types(types_list, plugin_package, types_package, inputfile)
+
+func_list, func_name = get_definitions(cfg.messages)
 dto_gen.generate_dtos(func_list, base_package, plugin_package, plugin_name.title(), dto_package, args.inputfile)
 jvpp_impl_gen.generate_jvpp(func_list, base_package, plugin_package, plugin_name, control_ping_class_fqn, dto_package, args.inputfile)
 callback_gen.generate_callbacks(func_list, base_package, plugin_package, plugin_name.title(), callback_package, dto_package, args.inputfile)
@@ -155,3 +145,5 @@ notification_gen.generate_notification_registry(func_list, base_package, plugin_
 jvpp_c_gen.generate_jvpp(func_list, plugin_name, args.inputfile)
 jvpp_future_facade_gen.generate_jvpp(func_list, base_package, plugin_package, plugin_name.title(), dto_package, callback_package, notification_package, future_package, args.inputfile)
 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.inputfile)
+
+print "Java API for %s generated successfully" % args.inputfile
index 1235479..a043c94 100644 (file)
@@ -280,7 +280,7 @@ def generate_dump_reply_dto(request_dto_name, base_package, plugin_package, dto_
     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 = 'jstring'
+    artificial_type = 'u8'
 
     # In case of already existing artificial reply dump DTO, just update it
     # Used for sub-dump dtos
diff --git a/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py b/vpp-api/java/jvpp/gen/jvppgen/jni_gen.py
new file mode 100644 (file)
index 0000000..4b03b31
--- /dev/null
@@ -0,0 +1,291 @@
+#!/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.
+
+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})""")
+
+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});
+""")
+
+variable_length_array_value_template = Template("""mp->${length_var_name}""")
+variable_length_array_template = Template("""clib_net_to_host_${length_field_type}(${value})""")
+
+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}));
+""")
+
+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}));
+""")
+
+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}));
+""")
+
+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});
+""")
+
+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]);
+        }
+
+        (*env)->ReleaseShortArrayElements(env,  ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
+        (*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name});
+    }
+""")
+
+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]);
+        }
+
+        (*env)->ReleaseIntArrayElements(env,  ${field_reference_name}, ${field_reference_name}ArrayElements, 0);
+        (*env)->SetObjectField(env, ${object_name}, ${field_reference_name}FieldId, ${field_reference_name});
+    }
+""")
+
+# 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});
+    }
+""")
+
+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"):
+    """
+    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
+    """
+
+    # 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});""")
+
+array_length_enforcement_template = Template("""
+        size_t max_size = ${field_length};
+        if (cnt > max_size) cnt = max_size;""")
+
+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});
+    }
+""")
+
+u16_array_struct_setter_template = Template("""
+    jshort * ${field_reference_name}ArrayElements = (*env)->GetShortArrayElements(env, ${field_reference_name}, NULL);
+    if (${field_reference_name}) {
+        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("""
+    jint * ${field_reference_name}ArrayElements = (*env)->GetIntArrayElements(env, ${field_reference_name}, NULL);
+    if (${field_reference_name}) {
+        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("""
+    jlong * ${field_reference_name}ArrayElements = (*env)->GetLongArrayElements(env, ${field_reference_name}, NULL);
+    if (${field_reference_name}) {
+        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_binding_for_type(field_type, c_name, field_reference_name, field_name, field_length,
+                                 is_variable_len_array, object_name="request"):
+    """
+    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_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
+    """
+    # 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
+    msg_initialization = 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)
+
+    # 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
index d63acd6..1a35a6c 100644 (file)
@@ -17,6 +17,9 @@
 import os, util
 from string import Template
 
+import jni_gen
+
+
 def is_manually_generated(f_name, plugin_name):
     return f_name in {'control_ping_reply'}
 
@@ -54,6 +57,7 @@ static void delete_class_references(JNIEnv* env) {
     $delete_class_invocations
 }""")
 
+
 def generate_class_cache(func_list, plugin_name):
     class_references = []
     find_class_invocations = []
@@ -105,94 +109,10 @@ 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 ${java_name}FieldId = (*env)->GetFieldID(env, requestClass, "${java_name}", "${jni_signature}");
-    ${jni_type} ${java_name} = (*env)->Get${jni_getter}(env, request, ${java_name}FieldId);
+    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);
     """)
 
-u8_struct_setter_template = Template("""
-    mp->${c_name} = ${java_name};""")
-
-u16_struct_setter_template = Template("""
-    mp->${c_name} = clib_host_to_net_u16(${java_name});""")
-
-u32_struct_setter_template = Template("""
-    mp->${c_name} = clib_host_to_net_u32(${java_name});""")
-
-i32_struct_setter_template = Template("""
-    mp->${c_name} = clib_host_to_net_i32(${java_name});!""")
-
-u64_struct_setter_template = Template("""
-    mp->${c_name} = clib_host_to_net_u64(${java_name});""")
-
-fixed_array_length_enforcement_template = Template("""
-        size_t max_size = ${field_length};
-        if (cnt > max_size) cnt = max_size;""")
-
-u8_array_struct_setter_template = Template("""
-    if (${java_name}) {
-        jsize cnt = (*env)->GetArrayLength (env, ${java_name});
-        ${field_length_check}
-        (*env)->GetByteArrayRegion(env, ${java_name}, 0, cnt, (jbyte *)mp->${c_name});
-    }
-""")
-
-u16_array_struct_setter_template = Template("""
-    jshort * ${java_name}ArrayElements = (*env)->GetShortArrayElements(env, ${java_name}, NULL);
-    if (${java_name}) {
-        size_t _i;
-        jsize cnt = (*env)->GetArrayLength (env, ${java_name});
-        ${field_length_check}
-        for (_i = 0; _i < cnt; _i++) {
-            mp->${c_name}[_i] = clib_host_to_net_u16(${java_name}ArrayElements[_i]);
-        }
-    }
-    (*env)->ReleaseShortArrayElements (env, ${java_name}, ${java_name}ArrayElements, 0);
-    """)
-
-u32_array_struct_setter_template = Template("""
-    jint * ${java_name}ArrayElements = (*env)->GetIntArrayElements(env, ${java_name}, NULL);
-    if (${java_name}) {
-        size_t _i;
-        jsize cnt = (*env)->GetArrayLength (env, ${java_name});
-        ${field_length_check}
-        for (_i = 0; _i < cnt; _i++) {
-            mp->${c_name}[_i] = clib_host_to_net_u32(${java_name}ArrayElements[_i]);
-        }
-    }
-    (*env)->ReleaseIntArrayElements (env, ${java_name}, ${java_name}ArrayElements, 0);
-    """)
-
-u64_array_struct_setter_template = Template("""
-    jlong * ${java_name}ArrayElements = (*env)->GetLongArrayElements(env, ${java_name}, NULL);
-    if (${java_name}) {
-        size_t _i;
-        jsize cnt = (*env)->GetArrayLength (env, ${java_name});
-        ${field_length_check}
-        for (_i = 0; _i < cnt; _i++) {
-            mp->${c_name}[_i] = clib_host_to_net_u64(${java_name}ArrayElements[_i]);
-        }
-    }
-    (*env)->ReleaseLongArrayElements (env, ${java_name}, ${java_name}ArrayElements, 0);
-    """)
-
-vl_api_ip4_fib_counter_t_array_struct_setter_template = Template("""
-    // vl_api_ip4_fib_counter_t_array_field_setter_template FIXME""")
-
-vl_api_ip6_fib_counter_t_array_struct_setter_template = Template("""
-    // vl_api_ip6_fib_counter_t_array_field_setter_template FIXME""")
-
-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,
-                          'vl_api_ip4_fib_counter_t[]': vl_api_ip4_fib_counter_t_array_struct_setter_template,
-                          'vl_api_ip6_fib_counter_t[]': vl_api_ip6_fib_counter_t_array_struct_setter_template
-                  }
 
 jni_impl_template = Template("""
 /**
@@ -200,13 +120,12 @@ jni_impl_template = Template("""
  * Generated based on $inputfile preparsed data:
 $api_data
  */
-JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_${plugin_name}_JVpp${java_plugin_name}Impl_${java_name}0
+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
-    $field_identifiers
 
     // create message:
     mp = vl_msg_api_alloc(sizeof(*mp));
@@ -215,7 +134,8 @@ JNIEXPORT jint JNICALL Java_io_fd_vpp_jvpp_${plugin_name}_JVpp${java_plugin_name
     mp->client_index = plugin_main->my_client_index;
     mp->context = clib_host_to_net_u32 (my_context_id);
 
-    $struct_setters
+    $msg_initialization
+
     // send message:
     vl_msg_api_send_shmem (plugin_main->vl_input_queue, (u8 *)&mp);
     if ((*env)->ExceptionCheck(env)) {
@@ -235,8 +155,7 @@ def generate_jni_impl(func_list, plugin_name, inputfile):
 
         arguments = ''
         request_class = ''
-        field_identifiers = ''
-        struct_setters = ''
+        msg_initialization = ''
         f_name_uppercase = f_name.upper()
 
         if f['args']:
@@ -247,142 +166,29 @@ def generate_jni_impl(func_list, plugin_name, inputfile):
                     java_name_upper=camel_case_function_name_upper,
                     plugin_name=plugin_name)
 
-            # field identifiers
-            for t in zip(f['types'], f['args']):
-                jni_type = t[0]
-                java_field_name = util.underscore_to_camelcase(t[1])
-                jni_signature = util.jni_2_signature_mapping[jni_type]
-                jni_getter = util.jni_field_accessors[jni_type]
-                field_identifiers += request_field_identifier_template.substitute(
-                        jni_type=jni_type,
-                        java_name=java_field_name,
-                        jni_signature=jni_signature,
-                        jni_getter=jni_getter)
-
-            # field setters
-            for t in zip(f['c_types'], f['args'], f['lengths']):
-                c_type = t[0]
-                c_name = t[1]
-                field_length = t[2][0]
-                field_length_check = ""
-
-                # check if we are processing variable length array:
-                if t[2][1]:
-                    field_length = util.underscore_to_camelcase(t[2][0])
-
-                # enforce max length if array has fixed length or uses variable length syntax
-                if str(t[2][0]) != "0":
-                    field_length_check = fixed_array_length_enforcement_template.substitute(field_length=field_length)
-
-                java_field_name = util.underscore_to_camelcase(c_name)
-
-                struct_setter_template = struct_setter_templates[c_type]
-
-                struct_setters += struct_setter_template.substitute(
-                        c_name=c_name,
-                        java_name=java_field_name,
-                        field_length_check=field_length_check)
+            for t in zip(f['types'], f['args'], f['lengths']):
+                field_name = util.underscore_to_camelcase(t[1])
+                msg_initialization += jni_gen.jni_request_binding_for_type(field_type=t[0], c_name=t[1],
+                                                                           field_reference_name=field_name,
+                                                                           field_name=field_name,
+                                                                           field_length=t[2][0],
+                                                                           is_variable_len_array=t[2][1])
 
         jni_impl.append(jni_impl_template.substitute(
                 inputfile=inputfile,
                 api_data=util.api_message_to_javadoc(f),
-                java_name=camel_case_function_name,
+                field_reference_name=camel_case_function_name,
+                field_name=camel_case_function_name,
                 c_name_uppercase=f_name_uppercase,
                 c_name=f_name,
                 plugin_name=plugin_name,
                 java_plugin_name=plugin_name.title(),
                 request_class=request_class,
-                field_identifiers=field_identifiers,
-                struct_setters=struct_setters,
+                msg_initialization=msg_initialization,
                 args=arguments))
 
     return "\n".join(jni_impl)
 
-
-dto_field_id_template = Template("""
-    jfieldID ${java_name}FieldId = (*env)->GetFieldID(env, ${class_ref_name}Class, "${java_name}", "${jni_signature}");""")
-
-default_dto_field_setter_template = Template("""
-    (*env)->Set${jni_setter}(env, dto, ${java_name}FieldId, mp->${c_name});
-""")
-
-variable_length_array_value_template = Template("""mp->${length_var_name}""")
-variable_length_array_template = Template("""clib_net_to_host_${length_field_type}(${value})""")
-
-u16_dto_field_setter_template = Template("""
-    (*env)->Set${jni_setter}(env, dto, ${java_name}FieldId, clib_net_to_host_u16(mp->${c_name}));
-""")
-
-u32_dto_field_setter_template = Template("""
-    (*env)->Set${jni_setter}(env, dto, ${java_name}FieldId, clib_net_to_host_u32(mp->${c_name}));
-""")
-
-u64_dto_field_setter_template = Template("""
-    (*env)->Set${jni_setter}(env, dto, ${java_name}FieldId, clib_net_to_host_u64(mp->${c_name}));
-""")
-
-u8_array_dto_field_setter_template = Template("""
-    jbyteArray ${java_name} = (*env)->NewByteArray(env, ${field_length});
-    (*env)->SetByteArrayRegion(env, ${java_name}, 0, ${field_length}, (const jbyte*)mp->${c_name});
-    (*env)->SetObjectField(env, dto, ${java_name}FieldId, ${java_name});
-""")
-
-u16_array_dto_field_setter_template = Template("""
-    {
-        jshortArray ${java_name} = (*env)->NewShortArray(env, ${field_length});
-        jshort * ${java_name}ArrayElements = (*env)->GetShortArrayElements(env, ${java_name}, NULL);
-        unsigned int _i;
-        for (_i = 0; _i < ${field_length}; _i++) {
-            ${java_name}ArrayElements[_i] = clib_net_to_host_u16(mp->${c_name}[_i]);
-        }
-
-        (*env)->ReleaseShortArrayElements(env,  ${java_name}, ${java_name}ArrayElements, 0);
-        (*env)->SetObjectField(env, dto, ${java_name}FieldId, ${java_name});
-    }
-""")
-
-u32_array_dto_field_setter_template = Template("""
-    {
-        jintArray ${java_name} = (*env)->NewIntArray(env, ${field_length});
-        jint * ${java_name}ArrayElements = (*env)->GetIntArrayElements(env, ${java_name}, NULL);
-        unsigned int _i;
-        for (_i = 0; _i < ${field_length}; _i++) {
-            ${java_name}ArrayElements[_i] = clib_net_to_host_u32(mp->${c_name}[_i]);
-        }
-
-        (*env)->ReleaseIntArrayElements(env,  ${java_name}, ${java_name}ArrayElements, 0);
-        (*env)->SetObjectField(env, dto, ${java_name}FieldId, ${java_name});
-    }
-""")
-
-# 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 ${java_name} = (*env)->NewLongArray(env, ${field_length});
-        jlong * ${java_name}ArrayElements = (*env)->GetLongArrayElements(env, ${java_name}, NULL);
-        unsigned int _i;
-        for (_i = 0; _i < ${field_length}; _i++) {
-            ${java_name}ArrayElements[_i] = clib_net_to_host_u64(mp->${c_name}[_i]);
-        }
-
-        (*env)->ReleaseLongArrayElements(env,  ${java_name}, ${java_name}ArrayElements, 0);
-        (*env)->SetObjectField(env, dto, ${java_name}FieldId, ${java_name});
-    }
-""")
-
-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
-                      }
-
 # 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
@@ -418,6 +224,7 @@ static void vl_api_${handler_name}_t_handler (vl_api_${handler_name}_t * mp)
     (*env)->CallVoidMethod(env, plugin_main->callbackObject, callbackMethod, dto);
 }""")
 
+
 def generate_msg_handlers(func_list, plugin_name, inputfile):
     handlers = []
     for f in func_list:
@@ -438,25 +245,20 @@ def generate_msg_handlers(func_list, plugin_name, inputfile):
         dto_setters = ''
         err_handler = ''
         # dto setters
-        for t in zip(f['c_types'], f['types'], f['args'], f['lengths']):
-            c_type = t[0]
-            jni_type = t[1]
-            c_name = t[2]
-            field_length = t[3][0]
-
-            if jni_type.endswith('Array') and field_length == '0':
-                raise Exception('Variable array \'%s\' defined in message \'%s\' '
-                                'should have defined length (e.g. \'%s[%s_length]\''
-                                % (c_name, handler_name, c_name, c_name))
-
-            # check if we are processing variable length array
-            if t[3][1]:
-                length_var_name = t[3][0]
-                length_field_type = f['c_types'][f['args'].index(length_var_name)]
-                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 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):
@@ -465,24 +267,6 @@ def generate_msg_handlers(func_list, plugin_name, inputfile):
                 )
                 continue
 
-            java_field_name = util.underscore_to_camelcase(c_name)
-            jni_signature = util.jni_2_signature_mapping[jni_type]
-            jni_setter = util.jni_field_accessors[jni_type]
-
-            dto_setters += dto_field_id_template.substitute(
-                    java_name=java_field_name,
-                    class_ref_name=ref_name,
-                    jni_signature=jni_signature)
-
-            dto_setter_template = dto_field_setter_templates[c_type]
-
-            dto_setters += dto_setter_template.substitute(
-                    java_name=java_field_name,
-                    jni_signature=jni_signature,
-                    c_name=c_name,
-                    jni_setter=jni_setter,
-                    field_length=field_length)
-
         handlers.append(msg_handler_template.substitute(
             inputfile=inputfile,
             api_data=util.api_message_to_javadoc(f),
diff --git a/vpp-api/java/jvpp/gen/jvppgen/types_gen.py b/vpp-api/java/jvpp/gen/jvppgen/types_gen.py
new file mode 100644 (file)
index 0000000..d12fb3d
--- /dev/null
@@ -0,0 +1,227 @@
+#!/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
+from string import Template
+
+import util
+import jni_gen
+import dto_gen
+
+type_template = Template("""
+package $plugin_package.$type_package;
+
+/**
+ * <p>This class represents $c_type_name type definition.
+ * <br>It was generated by types_gen.py based on $inputfile preparsed data:
+ * <pre>
+$docs
+ * </pre>
+ */
+public final class $java_type_name {
+$fields
+$methods
+}
+""")
+
+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});
+    }
+""")
+
+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);
+        unsigned int _i;
+        for (_i = 0; _i < ${field_length}; _i++) {
+            jmethodID ${field_reference_name}Constructor = (*env)->GetMethodID(env, ${field_reference_name}Class, "<init>", "()V");
+            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)->SetObjectField(env, dto, ${field_reference_name}FieldId, ${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_binding_for_type(field_type=t[0], c_name=c_name_prefix + t[1],
+                                                                     field_reference_name=field_reference_name,
+                                                                     field_name=field_name,
+                                                                     field_length=t[2][0],
+                                                                     is_variable_len_array=t[2][1],
+                                                                     object_name=object_name)
+    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):
+    """
+    Generates Java representation of custom types defined in api file.
+    """
+
+    #
+    if not types_list:
+        print "Skipping custom types generation (%s does not define custom types)." % inputfile
+        return
+
+    print "Generating custom types"
+
+    if not os.path.exists(types_package):
+        raise Exception("%s folder is missing" % 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()
+
index 712f178..4d3fca3 100644 (file)
@@ -65,78 +65,94 @@ def get_reply_suffix(name):
             else:
                 return reply_suffix
 
-# http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/types.html
-jni_2_java_type_mapping = {'jbyte': 'byte',
-                           'jbyteArray': 'byte[]',
-                           'jchar': 'char',
-                           'jcharArray': 'char[]',
-                           'jshort': 'short',
-                           'jshortArray': 'short[]',
-                           'jint': 'int',
-                           'jintArray': 'int[]',
-                           'jlong': 'long',
-                           'jlongArray': 'long[]',
-                           'jdouble': 'double',
-                           'jdoubleArray': 'double[]',
-                           'jfloat': 'float',
-                           'jfloatArray': 'float[]',
-                           'void': 'void',
-                           'jstring': 'java.lang.String',
-                           'jobject': 'java.lang.Object',
-                           'jobjectArray': 'java.lang.Object[]'
-                           }
-
-# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/types.html#type_signatures
-jni_2_signature_mapping = {'jbyte': 'B',
-                           'jbyteArray': '[B',
-                           'jchar': 'C',
-                           'jcharArray': '[C',
-                           'jshort': 'S',
-                           'jshortArray': '[S',
-                           'jint': 'I',
-                           'jintArray': '[I',
-                           'jlong': 'J',
-                           'jlongArray': '[J',
-                           'jdouble': 'D',
-                           'jdoubleArray': '[D',
-                           'jfloat': 'F',
-                           'jfloatArray': '[F'
-                           }
-
-# https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#Get_type_Field_routines
-jni_field_accessors = {
-    'jbyte': 'ByteField',
-    'jbyteArray': 'ObjectField',
-    'jchar': 'CharField',
-    'jcharArray': 'ObjectField',
-    'jshort': 'ShortField',
-    'jshortArray': 'ObjectField',
-    'jint': 'IntField',
-    'jintArray': 'ObjectField',
-    'jlong': 'LongField',
-    'jlongArray': 'ObjectField',
-    'jdouble': 'DoubleField',
-    'jdoubleArray': 'ObjectField',
-    'jfloat': 'FloatField',
-    'jfloatArray': 'ObjectField'
-}
-
 # 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',
+                          'u8[]': 'jbyteArray',
                           'u16': 'jshort',
+                          'u16[]': 'jshortArray',
                           'i16': 'jshort',
+                          'i16[]': 'jshortArray',
                           'u32': 'jint',
+                          'u32[]': 'jintArray',
                           'i32': 'jint',
+                          'i32[]': 'jintArray',
                           'u64': 'jlong',
+                          'u64[]': 'longArray',
                           'i64': 'jlong',
-                          'f64': 'jdouble'
+                          'u64[]': 'longArray',
+                          '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'
+                        }
+
+
 # vpe.api calls that do not follow naming conventions and have to be handled exceptionally when finding reply -> request mapping
 # FIXME in vpe.api
 unconventional_naming_rep_req = {