diff --git a/rcljava_common/CMakeLists.txt b/rcljava_common/CMakeLists.txt index 7f474bc2..49810795 100644 --- a/rcljava_common/CMakeLists.txt +++ b/rcljava_common/CMakeLists.txt @@ -36,6 +36,7 @@ set(${PROJECT_NAME}_java_sources "src/main/java/org/ros2/rcljava/common/JNIUtils.java" "src/main/java/org/ros2/rcljava/exceptions/RCLException.java" "src/main/java/org/ros2/rcljava/exceptions/RCLReturn.java" + "src/main/java/org/ros2/rcljava/interfaces/ActionDefinition.java" "src/main/java/org/ros2/rcljava/interfaces/Disposable.java" "src/main/java/org/ros2/rcljava/interfaces/MessageDefinition.java" "src/main/java/org/ros2/rcljava/interfaces/ServiceDefinition.java" diff --git a/rcljava_common/src/main/java/org/ros2/rcljava/interfaces/ActionDefinition.java b/rcljava_common/src/main/java/org/ros2/rcljava/interfaces/ActionDefinition.java new file mode 100644 index 00000000..9681463b --- /dev/null +++ b/rcljava_common/src/main/java/org/ros2/rcljava/interfaces/ActionDefinition.java @@ -0,0 +1,18 @@ +/* Copyright 2019 Open Source Robotics Foundation, Inc. + * + * 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 org.ros2.rcljava.interfaces; + +public interface ActionDefinition {} diff --git a/rosidl_generator_java/CMakeLists.txt b/rosidl_generator_java/CMakeLists.txt index 62705679..13b0c00d 100644 --- a/rosidl_generator_java/CMakeLists.txt +++ b/rosidl_generator_java/CMakeLists.txt @@ -24,19 +24,17 @@ endif() include(UseJava) include(JavaExtra) -set(CMAKE_JAVA_COMPILE_FLAGS "-source" "1.6" "-target" "1.6") - ament_index_register_resource("rosidl_generator_packages") if(BUILD_TESTING) find_package(rosidl_cmake REQUIRED) find_package(rosidl_generator_c REQUIRED) - - find_package(ament_lint_auto REQUIRED) + find_package(test_interface_files REQUIRED) find_package(ament_lint_auto REQUIRED) ament_lint_auto_find_test_dependencies() + # TODO(jacobperron): Use test_interface_files instead and update tests set(message_files "msg/Bool.msg" "msg/Byte.msg" @@ -70,7 +68,9 @@ if(BUILD_TESTING) "${CMAKE_CURRENT_SOURCE_DIR}/resource" ) - rosidl_generate_interfaces(${PROJECT_NAME} ${message_files} + rosidl_generate_interfaces(${PROJECT_NAME} + ${message_files} + ${test_interface_files_SRV_FILES} SKIP_INSTALL ) diff --git a/rosidl_generator_java/bin/rosidl_generator_java b/rosidl_generator_java/bin/rosidl_generator_java index 95ca2a86..a0f676d4 100755 --- a/rosidl_generator_java/bin/rosidl_generator_java +++ b/rosidl_generator_java/bin/rosidl_generator_java @@ -27,17 +27,13 @@ def main(argv=sys.argv[1:]): '--generator-arguments-file', required=True, help='The location of the file containing the generator arguments') - parser.add_argument( - '--typesupport-impl', - required=True, - help='The typesupport implementation targeted by the C interface') parser.add_argument( '--typesupport-impls', required=True, help='All the available typesupport implementations') args = parser.parse_args(argv) - return generate_java(args.generator_arguments_file, args.typesupport_impl, args.typesupport_impls) + return generate_java(args.generator_arguments_file, args.typesupport_impls.split(';')) if __name__ == '__main__': diff --git a/rosidl_generator_java/cmake/custom_command.cmake b/rosidl_generator_java/cmake/custom_command.cmake index 753c0835..db7d1cc1 100644 --- a/rosidl_generator_java/cmake/custom_command.cmake +++ b/rosidl_generator_java/cmake/custom_command.cmake @@ -11,19 +11,14 @@ # 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. - - add_custom_command( OUTPUT - ${_generated_msg_java_files} - ${_generated_msg_cpp_files} - ${_generated_srv_java_files} - ${_generated_srv_cpp_files} + ${_generated_extension_files} + ${_generated_java_files} COMMAND ${PYTHON_EXECUTABLE} ${rosidl_generator_java_BIN} --generator-arguments-file "${generator_arguments_file}" - --typesupport-impl "${_typesupport_impl}" --typesupport-impls "${_typesupport_impls}" - DEPENDS ${target_dependencies} + DEPENDS ${target_dependencies} ${rosidl_generate_interfaces_TARGET} COMMENT "Generating Java code for ROS interfaces" VERBATIM ) @@ -34,17 +29,14 @@ else() add_custom_target( ${rosidl_generate_interfaces_TARGET}${_target_suffix} DEPENDS - ${_generated_msg_java_files} - ${_generated_msg_cpp_files} - ${_generated_srv_java_files} - ${_generated_srv_cpp_files} + ${_generated_extension_files} + ${_generated_java_files} ) endif() add_jar("${PROJECT_NAME}_messages_jar" SOURCES - ${_generated_msg_java_files} - ${_generated_srv_java_files} + ${_generated_java_files} OUTPUT_NAME ${PROJECT_NAME}_messages INCLUDE_JARS diff --git a/rosidl_generator_java/cmake/register_java.cmake b/rosidl_generator_java/cmake/register_java.cmake index d3e0b659..bcc41108 100644 --- a/rosidl_generator_java/cmake/register_java.cmake +++ b/rosidl_generator_java/cmake/register_java.cmake @@ -13,9 +13,9 @@ # limitations under the License. macro(rosidl_generator_java_extras BIN GENERATOR_FILES TEMPLATE_DIR) - find_package(ament_cmake_core QUIET REQUIRED) + find_package(ament_cmake_core REQUIRED) ament_register_extension( - "rosidl_generate_interfaces" + "rosidl_generate_idl_interfaces" "rosidl_generator_java" "rosidl_generator_java_generate_interfaces.cmake") diff --git a/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake b/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake index deacaa6f..e0b0ff17 100644 --- a/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake +++ b/rosidl_generator_java/cmake/rosidl_generator_java_generate_interfaces.cmake @@ -39,8 +39,6 @@ if(NOT WIN32) endif() endif() -set(CMAKE_JAVA_COMPILE_FLAGS "-source" "1.6" "-target" "1.6") - # Get a list of typesupport implementations from valid rmw implementations. rosidl_generator_java_get_typesupports(_typesupport_impls) @@ -51,48 +49,53 @@ endif() set(_output_path "${CMAKE_CURRENT_BINARY_DIR}/rosidl_generator_java/${PROJECT_NAME}") -set(_generated_cpp_files "") -set(_generated_msg_java_files "") -set(_generated_msg_cpp_files "") -set(_generated_srv_java_files "") -set(_generated_srv_cpp_files "") +set(_generated_extension_files "") +set(_generated_java_files "") -foreach(_idl_file ${rosidl_generate_interfaces_IDL_FILES}) - get_filename_component(_parent_folder "${_idl_file}" DIRECTORY) - get_filename_component(_parent_folder "${_parent_folder}" NAME) - get_filename_component(_module_name "${_idl_file}" NAME_WE) +foreach(_typesupport_impl ${_typesupport_impls}) + set(_generated_extension_${_typesupport_impl}_files "") +endforeach() - if(_parent_folder STREQUAL "msg") - list(APPEND _generated_msg_java_files - "${_output_path}/${_parent_folder}/${_module_name}.java" +foreach(_abs_idl_file ${rosidl_generate_interfaces_ABS_IDL_FILES}) + get_filename_component(_parent_folder "${_abs_idl_file}" DIRECTORY) + get_filename_component(_parent_folder "${_parent_folder}" NAME) + get_filename_component(_idl_name "${_abs_idl_file}" NAME_WE) + list(APPEND _generated_java_files + "${_output_path}/${_parent_folder}/${_idl_name}.java" + ) + # TODO(jacobperron): Is there a more robust way of detecting services and actions + # Services generate extra files + if(_parent_folder STREQUAL "srv") + list(APPEND _generated_java_files + "${_output_path}/${_parent_folder}/${_idl_name}_Request.java" + "${_output_path}/${_parent_folder}/${_idl_name}_Response.java" ) - - foreach(_typesupport_impl ${_typesupport_impls}) - list_append_unique(_generated_msg_cpp_files - "${_output_path}/${_parent_folder}/${_module_name}.ep.${_typesupport_impl}.cpp" - ) - list(APPEND _type_support_by_generated_msg_cpp_files "${_typesupport_impl}") - endforeach() - elseif(_parent_folder STREQUAL "srv") - list(APPEND _generated_srv_java_files - "${_output_path}/${_parent_folder}/${_module_name}.java" + endif() + # Actions generate extra files + if(_parent_folder STREQUAL "action") + list(APPEND _generated_java_files + "${_output_path}/${_parent_folder}/${_idl_name}_Goal.java" + "${_output_path}/${_parent_folder}/${_idl_name}_Result.java" + "${_output_path}/${_parent_folder}/${_idl_name}_Feedback.java" ) - - foreach(_typesupport_impl ${_typesupport_impls}) - list_append_unique(_generated_srv_cpp_files - "${_output_path}/${_parent_folder}/${_module_name}.ep.${_typesupport_impl}.cpp" - ) - list(APPEND _type_support_by_generated_srv_cpp_files "${_typesupport_impl}") - endforeach() - else() - message(FATAL_ERROR "Interface file with unknown parent folder: ${_idl_file}") endif() + + foreach(_typesupport_impl ${_typesupport_impls}) + list(APPEND _generated_extension_${_typesupport_impl}_files "${_output_path}/${_parent_folder}/${_idl_name}.ep.${_typesupport_impl}.cpp") + endforeach() +endforeach() + +foreach(_typesupport_impl ${_typesupport_impls}) + foreach(_generated_file ${_generated_extension_${_typesupport_impl}_files}) + list(APPEND _generated_extension_files "${_generated_file}") + list(APPEND _typesupport_by_generated_cpp_files "${_typesupport_impl}") + endforeach() endforeach() set(_dependency_files "") set(_dependencies "") foreach(_pkg_name ${rosidl_generate_interfaces_DEPENDENCY_PACKAGE_NAMES}) - foreach(_idl_file ${${_pkg_name}_INTERFACE_FILES}) + foreach(_idl_file ${${_pkg_name}_IDL_FILES}) set(_abs_idl_file "${${_pkg_name}_DIR}/../${_idl_file}") normalize_path(_abs_idl_file "${_abs_idl_file}") list(APPEND _dependency_files "${_abs_idl_file}") @@ -103,11 +106,15 @@ endforeach() set(target_dependencies "${rosidl_generator_java_BIN}" ${rosidl_generator_java_GENERATOR_FILES} + "${rosidl_generator_java_TEMPLATE_DIR}/action.cpp.em" + "${rosidl_generator_java_TEMPLATE_DIR}/idl.cpp.em" "${rosidl_generator_java_TEMPLATE_DIR}/msg.cpp.em" "${rosidl_generator_java_TEMPLATE_DIR}/srv.cpp.em" + "${rosidl_generator_java_TEMPLATE_DIR}/action.java.em" + "${rosidl_generator_java_TEMPLATE_DIR}/idl.java.em" "${rosidl_generator_java_TEMPLATE_DIR}/msg.java.em" "${rosidl_generator_java_TEMPLATE_DIR}/srv.java.em" - ${rosidl_generate_interfaces_IDL_FILES} + ${rosidl_generate_interfaces_ABS_IDL_FILES} ${_dependency_files}) foreach(dep ${target_dependencies}) if(NOT EXISTS "${dep}") @@ -119,7 +126,7 @@ set(generator_arguments_file "${CMAKE_BINARY_DIR}/rosidl_generator_java__argumen rosidl_write_generator_arguments( "${generator_arguments_file}" PACKAGE_NAME "${PROJECT_NAME}" - ROS_INTERFACE_FILES "${rosidl_generate_interfaces_IDL_FILES}" + IDL_TUPLES "${rosidl_generate_interfaces_IDL_TUPLES}" ROS_INTERFACE_DEPENDENCIES "${_dependencies}" OUTPUT_DIR "${_output_path}" TEMPLATE_DIR "${rosidl_generator_java_TEMPLATE_DIR}" @@ -128,7 +135,6 @@ rosidl_write_generator_arguments( file(MAKE_DIRECTORY "${_output_path}") -set(_generated_extension_files "") set(_extension_dependencies "") set(_target_suffix "__java") @@ -156,10 +162,8 @@ add_subdirectory("${_subdir}" ${rosidl_generate_interfaces_TARGET}${_target_suff set_property( SOURCE - ${_generated_msg_java_files} - ${_generated_msg_cpp_files} - ${_generated_srv_java_files} - ${_generated_srv_cpp_files} + ${_generated_extension_files} + ${_generated_java_files} PROPERTY GENERATED 1) macro(set_properties _build_type) @@ -172,28 +176,21 @@ macro(set_properties _build_type) OUTPUT_NAME "${_library_path}") endmacro() -set(_type_support_by_generated_cpp_files ${_type_support_by_generated_msg_cpp_files} ${_type_support_by_generated_srv_cpp_files}) -set(_generated_cpp_files ${_generated_msg_cpp_files} ${_generated_srv_cpp_files}) - -set(_javaext_suffix "__javaext") -foreach(_generated_cpp_file ${_generated_cpp_files}) - get_filename_component(_full_folder "${_generated_cpp_file}" DIRECTORY) - get_filename_component(_package_folder "${_full_folder}" DIRECTORY) - get_filename_component(_package_name "${_package_folder}" NAME) - get_filename_component(_parent_folder "${_full_folder}" NAME) - get_filename_component(_base_msg_name "${_generated_cpp_file}" NAME_WE) - list(FIND _generated_cpp_files ${_generated_cpp_file} _file_index) - list(GET _type_support_by_generated_cpp_files ${_file_index} _typesupport_impl) +foreach(_generated_cpp_file ${_generated_extension_files}) + list(FIND _generated_extension_files ${_generated_cpp_file} _file_index) + list(GET _typesupport_by_generated_cpp_files ${_file_index} _typesupport_impl) find_package(${_typesupport_impl} REQUIRED) - set(_generated_msg_cpp_common_file "${_full_folder}/${_base_msg_name}.cpp") + get_filename_component(_parent_folder "${_generated_cpp_file}" DIRECTORY) + get_filename_component(_parent_folder "${_parent_folder}" NAME) + get_filename_component(_parent_folder "${_parent_folder}" NAME) + get_filename_component(_base_name "${_generated_cpp_file}" NAME_WE) string(REGEX REPLACE "^rosidl_typesupport_" "" _short_typesupport_impl ${_typesupport_impl}) - set(_library_name - "${_parent_folder}${_base_msg_name}${_short_typesupport_impl}" - ) + set(_library_name "${_parent_folder}${_base_name}${_short_typesupport_impl}") set(_library_path - "${_package_name}_${_parent_folder}_${_base_msg_name}__jni__${_typesupport_impl}" + "${rosidl_generate_interfaces_TARGET}_${_parent_folder}_${_base_name}__jni__${_typesupport_impl}" ) string_camel_case_to_lower_case_underscore(${_library_path} _library_path) + add_library(${_library_name} SHARED ${_generated_cpp_file} ) @@ -201,6 +198,7 @@ foreach(_generated_cpp_file ${_generated_cpp_files}) ${_library_name} ${rosidl_generate_interfaces_TARGET}${_target_suffix} ${rosidl_generate_interfaces_TARGET}__rosidl_typesupport_c + ${_target_dependencies} ) set(_extension_compile_flags "") if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") @@ -248,9 +246,6 @@ foreach(_generated_cpp_file ${_generated_cpp_files}) ) target_link_libraries(${_library_name} ${${_pkg_name}_JNI_LIBRARIES}) endforeach() - add_dependencies(${_library_name} - ${rosidl_generate_interfaces_TARGET}__${_typesupport_impl} - ) if(NOT rosidl_generate_interfaces_SKIP_INSTALL) install(TARGETS ${_library_name} ARCHIVE DESTINATION lib/jni @@ -277,7 +272,7 @@ add_custom_command( if(NOT rosidl_generate_interfaces_SKIP_INSTALL) set(_install_jar_dir "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}") - if(NOT _generated_msg_java_files STREQUAL "" OR NOT _generated_srv_java_files STREQUAL "") + if(NOT _generated_java_files STREQUAL "") install_jar("${PROJECT_NAME}_messages_jar" "share/${PROJECT_NAME}/java") ament_export_jars("share/${PROJECT_NAME}/java/${PROJECT_NAME}_messages.jar") endif() @@ -285,10 +280,8 @@ endif() if(BUILD_TESTING AND rosidl_generate_interfaces_ADD_LINTER_TESTS) if( - NOT _generated_msg_java_files STREQUAL "" OR - NOT _generated_msg_cpp_files STREQUAL "" OR - NOT _generated_srv_java_files STREQUAL "" OR - NOT _generated_srv_cpp_files STREQUAL "" + NOT _generated_java_files STREQUAL "" OR + NOT _generated_extension_files STREQUAL "" ) find_package(ament_cmake_cppcheck REQUIRED) ament_cppcheck( diff --git a/rosidl_generator_java/package.xml b/rosidl_generator_java/package.xml index 9d47a6c7..deffa059 100644 --- a/rosidl_generator_java/package.xml +++ b/rosidl_generator_java/package.xml @@ -35,15 +35,18 @@ rmw_implementation_cmake rosidl_generator_c - rosidl_parser rosidl_cmake + rosidl_parser rosidl_typesupport_c rosidl_typesupport_connext_c + rosidl_typesupport_fastrtps_c rosidl_typesupport_introspection_c rosidl_typesupport_opensplice_c + test_interface_files rosidl_generator_packages + rosidl_runtime_packages ament_cmake diff --git a/rosidl_generator_java/resource/action.cpp.em b/rosidl_generator_java/resource/action.cpp.em new file mode 100644 index 00000000..17e456bf --- /dev/null +++ b/rosidl_generator_java/resource/action.cpp.em @@ -0,0 +1,48 @@ +@# Included from rosidl_generator_java/resource/idl.cpp.em +@{ +from rosidl_generator_c import idl_structure_type_to_c_include_prefix + +action_includes = [ + 'rosidl_generator_c/action_type_support_struct.h', +] +}@ +@[for include in action_includes]@ +@[ if include in include_directives]@ +// already included above +// @ +@[ else]@ +@{include_directives.add(include)}@ +@[ end if]@ +#include "@(include)" +@[end for]@ + +#include "@(idl_structure_type_to_c_include_prefix(action.namespaced_type)).h" + +#ifdef __cplusplus +extern "C" { +#endif + +@{ +from rosidl_generator_java import get_jni_mangled_name + +action_fqn = action.namespaced_type.namespaced_name() +underscore_separated_type_name = '_'.join(action_fqn) +underscore_separated_jni_type_name = get_jni_mangled_name(action_fqn) +}@ +/* + * Class: @(underscore_separated_type_name) + * Method: getActionTypeSupport + * Signature: ()J + */ +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getActionTypeSupport(JNIEnv *, jclass); + +#ifdef __cplusplus +} +#endif + +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getActionTypeSupport(JNIEnv *, jclass) +{ + const rosidl_action_type_support_t * ts = ROSIDL_GET_ACTION_TYPE_SUPPORT( + @(package_name), @(action.namespaced_type.name)); + return reinterpret_cast(ts); +} diff --git a/rosidl_generator_java/resource/action.java.em b/rosidl_generator_java/resource/action.java.em new file mode 100644 index 00000000..070ddd61 --- /dev/null +++ b/rosidl_generator_java/resource/action.java.em @@ -0,0 +1,79 @@ +@# Generation triggered from rosidl_generator_java/resource/idl.java.em +// generated from rosidl_generator_java/resource/action.java.em +// with input from @(package_name):@(interface_path) +// generated code does not contain a copyright notice + +package @(package_name + '.' + interface_path.parts[0]); +@{ +import os +from rosidl_cmake import expand_template + +namespaces = action.namespaced_type.namespaces +type_name = action.namespaced_type.name +goal_type_name = action.goal.structure.namespaced_type.name +result_type_name = action.result.structure.namespaced_type.name +feedback_type_name = action.feedback.structure.namespaced_type.name + +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} +data.update({'message': action.goal}) +output_file = os.path.join(output_dir, *namespaces[1:], goal_type_name + '.java') +expand_template( + 'msg.java.em', + data, + output_file, + template_basepath=template_basepath) + +data.update({'message': action.result}) +output_file = os.path.join(output_dir, *namespaces[1:], result_type_name + '.java') +expand_template( + 'msg.java.em', + data, + output_file, + template_basepath=template_basepath) + +data.update({'message': action.feedback}) +output_file = os.path.join(output_dir, *namespaces[1:], feedback_type_name + '.java') +expand_template( + 'msg.java.em', + data, + output_file, + template_basepath=template_basepath) + +action_imports = [ + 'org.ros2.rcljava.common.JNIUtils', + 'org.ros2.rcljava.interfaces.ActionDefinition', + 'org.slf4j.Logger', + 'org.slf4j.LoggerFactory', +] +}@ + +@[for action_import in action_imports]@ +import @(action_import); +@[end for]@ + +public class @(type_name) implements ActionDefinition { + + private static final Logger logger = LoggerFactory.getLogger(@(type_name).class); + + static { + try { + JNIUtils.loadTypesupport(@(type_name).class); + } catch (UnsatisfiedLinkError ule) { + logger.error("Native code library failed to load.\n" + ule); + System.exit(1); + } + } + + public static native long getActionTypeSupport(); + + public static final Class<@(type_name)_Goal> GoalType = @(type_name)_Goal.class; + + public static final Class<@(type_name)_Result> ResultType = @(type_name)_Result.class; + + public static final Class<@(type_name)_Feedback> FeedbackType = @(type_name)_Feedback.class; +} diff --git a/rosidl_generator_java/resource/idl.cpp.em b/rosidl_generator_java/resource/idl.cpp.em new file mode 100644 index 00000000..48098dd9 --- /dev/null +++ b/rosidl_generator_java/resource/idl.cpp.em @@ -0,0 +1,110 @@ +// generated from rosidl_generator_java/resource/idl.cpp.em +// with input from @(package_name):@(interface_path) +// generated code does not contain a copyright notice +@ +@####################################################################### +@# EmPy template for generating .cpp files +@# +@# Context: +@# - package_name (string) +@# - interface_path (Path relative to the directory named after the package) +@# - content (IdlContent, list of elements, e.g. Messages or Services) +@####################################################################### +@{ +include_directives = set() + +jni_includes = [ + 'jni.h', +] +include_directives.update(jni_includes) +std_includes = [ + 'cassert', + 'cstdint', + 'string', +] +include_directives.update(std_includes) +rosidl_includes = [ + 'rosidl_generator_c/message_type_support_struct.h', +] +include_directives.update(rosidl_includes) +rcljava_includes = [ + 'rcljava_common/exceptions.h', + 'rcljava_common/signatures.h', +] +include_directives.update(rcljava_includes) +}@ +@[for include in jni_includes]@ +#include <@(include)> +@[end for]@ + +@[for include in std_includes]@ +#include <@(include)> +@[end for]@ + +@[for include in rosidl_includes]@ +#include "@(include)" +@[end for]@ + +@[for include in rcljava_includes]@ +#include "@(include)" +@[end for]@ + +// Ensure that a jlong is big enough to store raw pointers +static_assert(sizeof(jlong) >= sizeof(std::intptr_t), "jlong must be able to store pointers"); + +using rcljava_common::exceptions::rcljava_throw_exception; + +@{ +jni_package_name = package_name.replace('_', '_1') +}@ +@ +@####################################################################### +@# Handle messages +@####################################################################### +@{ +from rosidl_parser.definition import Message +}@ +@[for message in content.get_elements_of_type(Message)]@ +@{ +TEMPLATE( + 'msg.cpp.em', + package_name=package_name, + jni_package_name=jni_package_name, + message=message, + include_directives=include_directives) +}@ +@[end for]@ +@ +@####################################################################### +@# Handle services +@####################################################################### +@{ +from rosidl_parser.definition import Service +}@ +@[for service in content.get_elements_of_type(Service)]@ +@{ +TEMPLATE( + 'srv.cpp.em', + package_name=package_name, + jni_package_name=jni_package_name, + service=service, + include_directives=include_directives) +}@ +@[end for]@ +@ +@####################################################################### +@# Handle actions +@####################################################################### +@{ +from rosidl_parser.definition import Action +}@ +@[for action in content.get_elements_of_type(Action)]@ +@{ +TEMPLATE( + 'action.cpp.em', + package_name=package_name, + jni_package_name=jni_package_name, + action=action, + include_directives=include_directives) +}@ +@[end for]@ diff --git a/rosidl_generator_java/resource/idl.java.em b/rosidl_generator_java/resource/idl.java.em new file mode 100644 index 00000000..f34d75c7 --- /dev/null +++ b/rosidl_generator_java/resource/idl.java.em @@ -0,0 +1,87 @@ +@####################################################################### +@# EmPy template for generating .java files +@# +@# Context: +@# - package_name (string) +@# - interface_path (Path relative to the directory named after the package) +@# - content (IdlContent, list of elements, e.g. Messages or Services) +@# Additional context: +@# - output_dir (Path) +@# - template_basepath (Path) +@####################################################################### +@ +@####################################################################### +@# Handle messages +@####################################################################### +@{ +import os + +from rosidl_cmake import expand_template +from rosidl_parser.definition import Action +from rosidl_parser.definition import Message +from rosidl_parser.definition import Service + +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} + +for message in content.get_elements_of_type(Message): + data.update({'message': message}) + type_name = message.structure.namespaced_type.name + namespaces = message.structure.namespaced_type.namespaces + output_file = os.path.join(output_dir, *namespaces[1:], type_name + '.java') + expand_template( + 'msg.java.em', + data, + output_file, + template_basepath=template_basepath) +}@ +@ +@####################################################################### +@# Handle services +@####################################################################### +@{ +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} + +for service in content.get_elements_of_type(Service): + data.update({'service': service}) + type_name = service.namespaced_type.name + namespaces = service.namespaced_type.namespaces + output_file = os.path.join(output_dir, *namespaces[1:], type_name + '.java') + expand_template( + 'srv.java.em', + data, + output_file, + template_basepath=template_basepath) +}@ +@ +@####################################################################### +@# Handle actions +@####################################################################### +@{ +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} + +for action in content.get_elements_of_type(Action): + data.update({'action': action}) + type_name = action.namespaced_type.name + namespaces = action.namespaced_type.namespaces + output_file = os.path.join(output_dir, *namespaces[1:], type_name + '.java') + expand_template( + 'action.java.em', + data, + output_file, + template_basepath=template_basepath) +}@ diff --git a/rosidl_generator_java/resource/msg.cpp.em b/rosidl_generator_java/resource/msg.cpp.em index e1d449d4..6d1f7877 100644 --- a/rosidl_generator_java/resource/msg.cpp.em +++ b/rosidl_generator_java/resource/msg.cpp.em @@ -1,85 +1,26 @@ -// generated from rosidl_generator_java/resource/msg.cpp.em -// generated code does not contain a copyright notice - -#include - -#include -#include -#include - -// Ensure that a jlong is big enough to store raw pointers -static_assert(sizeof(jlong) >= sizeof(std::intptr_t), "jlong must be able to store pointers"); - -#include "@(spec.base_type.pkg_name)/@(subfolder)/@(module_name).h" -#include "rosidl_generator_c/message_type_support_struct.h" - -#include "rosidl_generator_c/string.h" -#include "rosidl_generator_c/string_functions.h" - -#include "rosidl_generator_c/primitives_array.h" -#include "rosidl_generator_c/primitives_array_functions.h" - -#include "rcljava_common/exceptions.h" -#include "rcljava_common/signatures.h" - -@#JNI performance tips taken from http://planet.jboss.org/post/jni_performance_the_saga_continues - -using rcljava_common::exceptions::rcljava_throw_exception; - +@# Included from rosidl_generator_java/resource/idl.cpp.em @{ - from collections import defaultdict -def get_normalized_type(type_, subfolder='msg'): - return get_java_type(type_, use_primitives=False, subfolder=subfolder).replace('.', '__') - -def get_jni_type(type_, subfolder='msg'): - return get_java_type(type_, use_primitives=False, subfolder=subfolder).replace('.', '/') - -constructor_signatures = defaultdict(lambda: "()V") -constructor_signatures['java/lang/Boolean'] = "(Z)V" -constructor_signatures['java/lang/Byte'] = "(B)V" -constructor_signatures['java/lang/Character'] = "(C)V" -constructor_signatures['java/lang/Double'] = "(D)V" -constructor_signatures['java/lang/Float'] = "(F)V" -constructor_signatures['java/lang/Integer'] = "(I)V" -constructor_signatures['java/lang/Long'] = "(J)V" -constructor_signatures['java/lang/Short'] = "(S)V" -constructor_signatures['java/util/List'] = None - -value_methods = {} -value_methods['java/lang/Boolean'] = ("booleanValue", "()Z") -value_methods['java/lang/Byte'] = ("byteValue", "()B") -value_methods['java/lang/Character'] = ("charValue", "()C") -value_methods['java/lang/Double'] = ("doubleValue", "()D") -value_methods['java/lang/Float'] = ("floatValue", "()F") -value_methods['java/lang/Integer'] = ("intValue", "()I") -value_methods['java/lang/Long'] = ("longValue", "()J") -value_methods['java/lang/Short'] = ("shortValue", "()S") - -jni_signatures = {} -jni_signatures['java/lang/Boolean'] = "Z" -jni_signatures['java/lang/Byte'] = "B" -jni_signatures['java/lang/Character'] = "C" -jni_signatures['java/lang/Double'] = "D" -jni_signatures['java/lang/Float'] = "F" -jni_signatures['java/lang/Integer'] = "I" -jni_signatures['java/lang/Long'] = "J" -jni_signatures['java/lang/Short'] = "S" - -non_primitive_types = set() - -def get_constructor_signature(type_, subfolder='msg'): - return constructor_signatures[get_jni_type(type_, subfolder=subfolder)] - -def get_value_method(type_, subfolder='msg'): - return value_methods.get(get_jni_type(type_, subfolder=subfolder)) - -def get_jni_signature(type_, subfolder='msg'): - return jni_signatures.get(get_jni_type(type_, subfolder=subfolder)) - -msg_normalized_type = get_normalized_type(spec.base_type, subfolder=subfolder) -msg_jni_type = get_jni_type(spec.base_type, subfolder=subfolder) +from rosidl_generator_c import basetype_to_c +from rosidl_generator_c import idl_structure_type_to_c_include_prefix +from rosidl_generator_java import constructor_signatures +from rosidl_generator_java import get_java_type +from rosidl_generator_java import get_jni_signature +from rosidl_generator_java import get_jni_type +from rosidl_generator_java import get_normalized_type +from rosidl_generator_java import value_methods +from rosidl_parser.definition import AbstractGenericString +from rosidl_parser.definition import AbstractString +from rosidl_parser.definition import AbstractWString +from rosidl_parser.definition import AbstractNestedType +from rosidl_parser.definition import AbstractSequence +from rosidl_parser.definition import Array +from rosidl_parser.definition import BasicType +from rosidl_parser.definition import NamespacedType + +msg_normalized_type = '__'.join(message.structure.namespaced_type.namespaced_name()) +msg_jni_type = '/'.join(message.structure.namespaced_type.namespaced_name()) list_java_type = "java.util.List" array_list_java_type = "java.util.ArrayList" @@ -90,31 +31,49 @@ list_jni_type = "java/util/List" array_list_normalized_type = "java__util__ArrayList" array_list_jni_type = "java/util/ArrayList" +# Collect JNI types and includes cache = defaultdict(lambda: False) - cache[msg_normalized_type] = msg_jni_type - -# We do not cache strings because java.lang.String behaves differently - -unique_fields = set() - -for field in spec.fields: - if field.type.is_array: +namespaced_types = set() +includes = set() +for member in message.structure.members: + type_ = member.type + if isinstance(type_, AbstractNestedType): cache[list_normalized_type] = list_jni_type cache[array_list_normalized_type] = array_list_jni_type - - if not field.type.type == 'string': - cache[get_normalized_type(field.type)] = get_jni_type(field.type) - - if not field.type.is_primitive_type(): - non_primitive_types.add(get_jni_type(field.type)) - unique_fields.add((field.type.pkg_name, field.type.type)) + type_ = type_.value_type + if isinstance(type_, BasicType): + includes.add('rosidl_generator_c/primitives_sequence.h') + includes.add('rosidl_generator_c/primitives_sequence_functions.h') + + # We do not cache strings because java.lang.String behaves differently + if not isinstance(type_, AbstractGenericString): + cache[get_normalized_type(type_)] = get_jni_type(type_) + + if isinstance(type_, AbstractString): + includes.add('rosidl_generator_c/string.h') + includes.add('rosidl_generator_c/string_functions.h') + + if isinstance(type_, AbstractWString): + includes.add('rosidl_generator_c/u16string.h') + includes.add('rosidl_generator_c/u16string_functions.h') + + if isinstance(type_, NamespacedType): + namespaced_types.add(get_jni_type(type_)) + includes.add(idl_structure_type_to_c_include_prefix(type_) + '.h') }@ - -@[for field_pkg_name, field_type in unique_fields]@ -#include "@(field_pkg_name)/msg/@(convert_camel_case_to_lower_case_underscore(field_type)).h" -#include "@(field_pkg_name)/msg/@(convert_camel_case_to_lower_case_underscore(field_type))__functions.h" +@[for include in includes]@ +@[ if include in include_directives]@ +// already included above +// @ +@[ else]@ +@{include_directives.add(include)}@ +@[ end if]@ +#include "@(include)" @[end for]@ + +#include "@(idl_structure_type_to_c_include_prefix(message.structure.namespaced_type)).h" + #ifdef __cplusplus extern "C" { #endif @@ -126,15 +85,15 @@ JavaVM * g_vm = nullptr; @[for normalized_type, jni_type in cache.items()]@ jclass _j@(normalized_type)_class_global = nullptr; -@[if constructor_signatures[jni_type]]@ +@[ if constructor_signatures[jni_type]]@ jmethodID _j@(normalized_type)_constructor_global = nullptr; -@[end if]@ +@[ end if]@ -@[ if value_methods.get(jni_type)]@ +@[ if value_methods.get(jni_type)]@ jmethodID _j@(normalized_type)_value_global = nullptr; -@[ end if]@ +@[ end if]@ -@[ if jni_type in non_primitive_types]@ +@[ if jni_type in namespaced_types]@ jmethodID _j@(normalized_type)_from_java_converter_global = nullptr; using _j@(normalized_type)_from_java_signature = @(normalized_type) * (*)(jobject, @(normalized_type) *); jlong _j@(normalized_type)_from_java_converter_ptr_global = 0; @@ -144,255 +103,283 @@ jmethodID _j@(normalized_type)_to_java_converter_global = nullptr; using _j@(normalized_type)_to_java_signature = jobject (*)(@(normalized_type) *, jobject); jlong _j@(normalized_type)_to_java_converter_ptr_global = 0; _j@(normalized_type)_to_java_signature _j@(normalized_type)_to_java_function = nullptr; -@[ end if]@ +@[ end if]@ @[end for]@ } // namespace +@{ +from rosidl_generator_java import get_jni_mangled_name + +message_fqn = message.structure.namespaced_type.namespaced_name() +underscore_separated_type_name = '_'.join(message_fqn) +underscore_separated_jni_type_name = get_jni_mangled_name(message_fqn) +}@ /* - * Class: @(jni_package_name)_@(subfolder)_@(type_name) + * Class: @(underscore_separated_type_name) * Method: getFromJavaConverter * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getFromJavaConverter +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getFromJavaConverter (JNIEnv *, jclass); /* - * Class: @(jni_package_name)_@(subfolder)_@(type_name) + * Class: @(underscore_separated_type_name) * Method: getToJavaConverter * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getToJavaConverter +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getToJavaConverter (JNIEnv *, jclass); /* - * Class: @(jni_package_name)_@(subfolder)_@(type_name) + * Class: @(underscore_separated_type_name) * Method: getTypeSupport * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getTypeSupport +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getTypeSupport (JNIEnv *, jclass); /* - * Class: @(jni_package_name)_@(subfolder)_@(type_name) + * Class: @(underscore_separated_type_name) * Method: getDestructor * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getDestructor +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getDestructor (JNIEnv *, jclass); #ifdef __cplusplus } #endif -@# Avoid warnings about unused arguments if the message definition does not contain any fields -@[if spec.fields]@ -@(msg_normalized_type) * @(spec.base_type.pkg_name)_@(type_name)__convert_from_java(jobject _jmessage_obj, @(msg_normalized_type) * ros_message) +@# Avoid warnings about unused arguments if the message definition does not contain any members +@[if message.structure.members]@ +@(msg_normalized_type) * @(underscore_separated_type_name)__convert_from_java(jobject _jmessage_obj, @(msg_normalized_type) * ros_message) @[else]@ -@(msg_normalized_type) * @(spec.base_type.pkg_name)_@(type_name)__convert_from_java(jobject, @(msg_normalized_type) * ros_message) +@(msg_normalized_type) * @(underscore_separated_type_name)__convert_from_java(jobject, @(msg_normalized_type) * ros_message) @[end if]@ { JNIEnv * env = nullptr; // TODO(esteve): check return status assert(g_vm != nullptr); - g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8); assert(env != nullptr); if (ros_message == nullptr) { ros_message = @(msg_normalized_type)__create(); } -@[for field in spec.fields]@ +@[for member in message.structure.members]@ @{ -normalized_type = get_normalized_type(field.type) +normalized_type = get_normalized_type(member.type) }@ -@[ if field.type.is_array] - auto _jfield_@(field.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(field.name)", "L@(list_jni_type);"); - jobject _jlist_@(field.name)_object = env->GetObjectField(_jmessage_obj, _jfield_@(field.name)_fid); - - if (_jlist_@(field.name)_object != nullptr) { - jmethodID _jlist_@(field.name)_get_mid = env->GetMethodID(_j@(list_normalized_type)_class_global, "get", "(I)Ljava/lang/Object;"); -@[ if field.type.array_size is None or field.type.is_upper_bound]@ - jmethodID _jlist_@(field.name)_size_mid = env->GetMethodID(_j@(list_normalized_type)_class_global, "size", "()I"); - jint _jlist_@(field.name)_size = env->CallIntMethod(_jlist_@(field.name)_object, _jlist_@(field.name)_size_mid); -@[ if field.type.type == 'string']@ - if (!rosidl_generator_c__String__Array__init(&(ros_message->@(field.name)), _jlist_@(field.name)_size)) { +@[ if isinstance(member.type, AbstractNestedType)] + auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "L@(list_jni_type);"); + jobject _jlist_@(member.name)_object = env->GetObjectField(_jmessage_obj, _jfield_@(member.name)_fid); + + if (_jlist_@(member.name)_object != nullptr) { + jmethodID _jlist_@(member.name)_get_mid = env->GetMethodID(_j@(list_normalized_type)_class_global, "get", "(I)Ljava/lang/Object;"); +@[ if isinstance(member.type, AbstractSequence)]@ + jmethodID _jlist_@(member.name)_size_mid = env->GetMethodID(_j@(list_normalized_type)_class_global, "size", "()I"); + jint _jlist_@(member.name)_size = env->CallIntMethod(_jlist_@(member.name)_object, _jlist_@(member.name)_size_mid); +@[ if isinstance(member.type.value_type, AbstractString)]@ + if (!rosidl_generator_c__String__Sequence__init(&(ros_message->@(member.name)), _jlist_@(member.name)_size)) { rcljava_throw_exception(env, "java/lang/IllegalStateException", "unable to create String__Array ros_message"); } -@[ else]@ -@[ if field.type.is_primitive_type()]@ - if (!rosidl_generator_c__@(field.type.type)__Array__init(&(ros_message->@(field.name)), _jlist_@(field.name)_size)) { - rcljava_throw_exception(env, "java/lang/IllegalStateException", "unable to create @(field.type.type)__Array ros_message"); +@[ elif isinstance(member.type.value_type, AbstractWString)]@ + if (!rosidl_generator_c__U16String__Sequence__init(&(ros_message->@(member.name)), _jlist_@(member.name)_size)) { + rcljava_throw_exception(env, "java/lang/IllegalStateException", "unable to create U16String__Array ros_message"); } -@[ else]@ - if (!@(field.type.pkg_name)__msg__@(field.type.type)__Array__init(&(ros_message->@(field.name)), _jlist_@(field.name)_size)) { - rcljava_throw_exception(env, "java/lang/IllegalStateException", "unable to create @(field.type.type)__Array ros_message"); +@[ else]@ +@[ if isinstance(member.type.value_type, BasicType)]@ + if (!rosidl_generator_c__@(member.type.value_type.typename)__Sequence__init(&(ros_message->@(member.name)), _jlist_@(member.name)_size)) { + rcljava_throw_exception(env, "java/lang/IllegalStateException", "unable to create @(member.type.value_type)__Array ros_message"); } - -@[ end if]@ -@[ end if]@ - auto _dest_@(field.name) = ros_message->@(field.name).data; @[ else]@ - jint _jlist_@(field.name)_size = @(field.type.array_size); + if (!@('__'.join(member.type.value_type.namespaced_name()))__Sequence__init(&(ros_message->@(member.name)), _jlist_@(member.name)_size)) { + rcljava_throw_exception(env, "java/lang/IllegalStateException", "unable to create @(member.type.value_type)__Array ros_message"); + } - auto _dest_@(field.name) = ros_message->@(field.name); @[ end if]@ +@[ end if]@ + auto _dest_@(member.name) = ros_message->@(member.name).data; +@[ else]@ + jint _jlist_@(member.name)_size = @(member.type.size); + + auto _dest_@(member.name) = ros_message->@(member.name); +@[ end if]@ - for (jint i = 0; i < _jlist_@(field.name)_size; ++i) { - auto element = env->CallObjectMethod(_jlist_@(field.name)_object, _jlist_@(field.name)_get_mid, i); -@[ if field.type.is_primitive_type()]@ -@[ if field.type.type == 'string']@ - jstring _jfield_@(field.name)_value = static_cast(element); - if (_jfield_@(field.name)_value != nullptr) { - const char * _str@(field.name) = env->GetStringUTFChars(_jfield_@(field.name)_value, 0); + for (jint i = 0; i < _jlist_@(member.name)_size; ++i) { + auto element = env->CallObjectMethod(_jlist_@(member.name)_object, _jlist_@(member.name)_get_mid, i); +@[ if isinstance(member.type.value_type, AbstractString)]@ + jstring _jfield_@(member.name)_value = static_cast(element); + if (_jfield_@(member.name)_value != nullptr) { + const char * _str@(member.name) = env->GetStringUTFChars(_jfield_@(member.name)_value, 0); rosidl_generator_c__String__assign( - &_dest_@(field.name)[i], _str@(field.name)); - env->ReleaseStringUTFChars(_jfield_@(field.name)_value, _str@(field.name)); + &_dest_@(member.name)[i], _str@(member.name)); + env->ReleaseStringUTFChars(_jfield_@(member.name)_value, _str@(member.name)); + } +@[ elif isinstance(member.type.value_type, AbstractWString)]@ + jstring _jfield_@(member.name)_value = static_cast(element); + if (_jfield_@(member.name)_value != nullptr) { + const jchar * _str@(member.name) = env->GetStringChars(_jfield_@(member.name)_value, 0); + rosidl_generator_c__U16String__assign( + &_dest_@(member.name)[i], _str@(member.name)); + env->ReleaseStringChars(_jfield_@(member.name)_value, _str@(member.name)); } -@[ else]@ +@[ elif isinstance(member.type.value_type, BasicType)]@ @{ -call_method_name = 'Call%sMethod' % get_java_type(field.type, use_primitives=True).capitalize() +call_method_name = 'Call%sMethod' % get_java_type(member.type.value_type, use_primitives=True).capitalize() }@ - _dest_@(field.name)[i] = env->@(call_method_name)(element, _j@(normalized_type)_value_global); -@[ end if]@ -@[ else]@ - _dest_@(field.name)[i] = *_j@(normalized_type)_from_java_function(element, nullptr); -@[ end if]@ + _dest_@(member.name)[i] = env->@(call_method_name)(element, _j@(normalized_type)_value_global); +@[ else]@ + _dest_@(member.name)[i] = *_j@(normalized_type)_from_java_function(element, nullptr); +@[ end if]@ env->DeleteLocalRef(element); } } -@[ else]@ -@[ if field.type.is_primitive_type()]@ -@[ if field.type.type == 'string']@ - auto _jfield_@(field.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(field.name)", "Ljava/lang/String;"); - jstring _jvalue@(field.name) = static_cast(env->GetObjectField(_jmessage_obj, _jfield_@(field.name)_fid)); - - if (_jvalue@(field.name) != nullptr) { - const char * _str@(field.name) = env->GetStringUTFChars(_jvalue@(field.name), 0); +@[ else]@ +@[ if isinstance(member.type, AbstractGenericString)]@ + auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "Ljava/lang/String;"); + jstring _jvalue@(member.name) = static_cast(env->GetObjectField(_jmessage_obj, _jfield_@(member.name)_fid)); + + if (_jvalue@(member.name) != nullptr) { +@[ if isinstance(member.type, AbstractString)]@ + const char * _str@(member.name) = env->GetStringUTFChars(_jvalue@(member.name), 0); rosidl_generator_c__String__assign( - &ros_message->@(field.name), _str@(field.name)); - env->ReleaseStringUTFChars(_jvalue@(field.name), _str@(field.name)); + &ros_message->@(member.name), _str@(member.name)); + env->ReleaseStringUTFChars(_jvalue@(member.name), _str@(member.name)); +@[ else]@ + const jchar * _str@(member.name) = env->GetStringChars(_jvalue@(member.name), 0); + rosidl_generator_c__U16String__assign( + &ros_message->@(member.name), _str@(member.name)); + env->ReleaseStringChars(_jvalue@(member.name), _str@(member.name)); +@[ end if]@ } -@[ else]@ +@[ elif isinstance(member.type, BasicType)]@ @{ -jni_signature = get_jni_signature(field.type) -get_method_name = 'Get%sField' % get_java_type(field.type, use_primitives=True).capitalize() +jni_signature = get_jni_signature(member.type) +get_method_name = 'Get%sField' % get_java_type(member.type, use_primitives=True).capitalize() }@ - auto _jfield_@(field.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(field.name)", "@(jni_signature)"); - ros_message->@(field.name) = env->@(get_method_name)(_jmessage_obj, _jfield_@(field.name)_fid); + auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "@(jni_signature)"); + ros_message->@(member.name) = env->@(get_method_name)(_jmessage_obj, _jfield_@(member.name)_fid); -@[ end if]@ -@[ else]@ - auto _jfield_@(field.name)_fid = env->GetFieldID( - _j@(msg_normalized_type)_class_global, "@(field.name)", "L@(field.type.pkg_name)/msg/@(field.type.type);"); - assert(_jfield_@(field.name)_fid != nullptr); +@[ else]@ + auto _jfield_@(member.name)_fid = env->GetFieldID( + _j@(msg_normalized_type)_class_global, "@(member.name)", "L@('/'.join(member.type.namespaced_name()));"); + assert(_jfield_@(member.name)_fid != nullptr); - jobject _jfield_@(field.name)_obj = env->GetObjectField(_jmessage_obj, _jfield_@(field.name)_fid); + jobject _jfield_@(member.name)_obj = env->GetObjectField(_jmessage_obj, _jfield_@(member.name)_fid); - if (_jfield_@(field.name)_obj != nullptr) { - ros_message->@(field.name) = *_j@(normalized_type)_from_java_function(_jfield_@(field.name)_obj, nullptr); + if (_jfield_@(member.name)_obj != nullptr) { + ros_message->@(member.name) = *_j@(normalized_type)_from_java_function(_jfield_@(member.name)_obj, nullptr); } - env->DeleteLocalRef(_jfield_@(field.name)_obj); -@[ end if]@ + env->DeleteLocalRef(_jfield_@(member.name)_obj); @[ end if]@ +@[ end if]@ @[end for]@ assert(ros_message != nullptr); return ros_message; } @# Avoid warnings about unused arguments if the message definition does not contain any fields -@[if spec.fields]@ -jobject @(spec.base_type.pkg_name)_@(type_name)__convert_to_java(@(msg_normalized_type) * _ros_message, jobject _jmessage_obj) +@[if message.structure.members]@ +jobject @(underscore_separated_type_name)__convert_to_java(@(msg_normalized_type) * _ros_message, jobject _jmessage_obj) @[else]@ -jobject @(spec.base_type.pkg_name)_@(type_name)__convert_to_java(@(msg_normalized_type) *, jobject _jmessage_obj) +jobject @(underscore_separated_type_name)__convert_to_java(@(msg_normalized_type) *, jobject _jmessage_obj) @[end if]@ { JNIEnv * env = nullptr; // TODO(esteve): check return status assert(g_vm != nullptr); - g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6); + g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8); assert(env != nullptr); if (_jmessage_obj == nullptr) { _jmessage_obj = env->NewObject(_j@(msg_normalized_type)_class_global, _j@(msg_normalized_type)_constructor_global); } -@[for field in spec.fields]@ +@[for member in message.structure.members]@ @{ -normalized_type = get_normalized_type(field.type) +normalized_type = get_normalized_type(member.type) }@ -@[ if field.type.is_array]@ -@[ if field.type.is_primitive_type()]@ - auto _jfield_@(field.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(field.name)", "L@(list_jni_type);"); - jobject _jarray_list_@(field.name)_obj = env->NewObject(_j@(array_list_normalized_type)_class_global, _j@(array_list_normalized_type)_constructor_global); -@[ if field.type.array_size and not field.type.is_upper_bound]@ - for (size_t i = 0; i < @(field.type.array_size); ++i) { - auto _ros_@(field.name)_element = _ros_message->@(field.name)[i]; -@[ else]@ - for (size_t i = 0; i < _ros_message->@(field.name).size; ++i) { - auto _ros_@(field.name)_element = _ros_message->@(field.name).data[i]; -@[ end if]@ -@[ if field.type.type == 'string']@ - jobject _jlist_@(field.name)_element = nullptr; - if (_ros_@(field.name)_element.data != nullptr) { - _jlist_@(field.name)_element = env->NewStringUTF(_ros_@(field.name)_element.data); +@[ if isinstance(member.type, AbstractNestedType)]@ +@[ if isinstance(member.type.value_type, (BasicType, AbstractGenericString))]@ + auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "L@(list_jni_type);"); + jobject _jarray_list_@(member.name)_obj = env->NewObject(_j@(array_list_normalized_type)_class_global, _j@(array_list_normalized_type)_constructor_global); +@[ if isinstance(member.type, Array)]@ + for (size_t i = 0; i < @(member.type.size); ++i) { + auto _ros_@(member.name)_element = _ros_message->@(member.name)[i]; +@[ else]@ + for (size_t i = 0; i < _ros_message->@(member.name).size; ++i) { + auto _ros_@(member.name)_element = _ros_message->@(member.name).data[i]; +@[ end if]@ +@[ if isinstance(member.type.value_type, AbstractGenericString)]@ + jobject _jlist_@(member.name)_element = nullptr; + if (_ros_@(member.name)_element.data != nullptr) { +@[ if isinstance(member.type.value_type, AbstractString)]@ + _jlist_@(member.name)_element = env->NewStringUTF(_ros_@(member.name)_element.data); +@[ else]@ + _jlist_@(member.name)_element = env->NewString(_ros_@(member.name)_element.data, _ros_@(member.name)_element.size); +@[ end if]@ } -@[ else]@ - jobject _jlist_@(field.name)_element = env->NewObject( - _j@(normalized_type)_class_global, _j@(normalized_type)_constructor_global, _ros_@(field.name)_element); -@[ end if]@ +@[ else]@ + jobject _jlist_@(member.name)_element = env->NewObject( + _j@(normalized_type)_class_global, _j@(normalized_type)_constructor_global, _ros_@(member.name)_element); +@[ end if]@ // TODO(esteve): replace ArrayList with a jobjectArray to initialize the array beforehand - jmethodID _jlist_@(field.name)_add_mid = env->GetMethodID( + jmethodID _jlist_@(member.name)_add_mid = env->GetMethodID( _j@(array_list_normalized_type)_class_global, "add", "(Ljava/lang/Object;)Z"); - if (_jlist_@(field.name)_element != nullptr) { - jboolean _jlist_@(field.name)_add_result = env->CallBooleanMethod(_jarray_list_@(field.name)_obj, _jlist_@(field.name)_add_mid, _jlist_@(field.name)_element); - assert(_jlist_@(field.name)_add_result); + if (_jlist_@(member.name)_element != nullptr) { + jboolean _jlist_@(member.name)_add_result = env->CallBooleanMethod(_jarray_list_@(member.name)_obj, _jlist_@(member.name)_add_mid, _jlist_@(member.name)_element); + assert(_jlist_@(member.name)_add_result); } } -@[ else]@ +@[ else]@ - auto _jfield_@(field.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(field.name)", "L@(list_jni_type);"); - jobject _jarray_list_@(field.name)_obj = env->NewObject(_j@(array_list_normalized_type)_class_global, _j@(array_list_normalized_type)_constructor_global); + auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "L@(list_jni_type);"); + jobject _jarray_list_@(member.name)_obj = env->NewObject(_j@(array_list_normalized_type)_class_global, _j@(array_list_normalized_type)_constructor_global); -@[ if field.type.array_size and not field.type.is_upper_bound]@ - for (size_t i = 0; i < @(field.type.array_size); ++i) { - jobject _jlist_@(field.name)_element = _j@(normalized_type)_to_java_function(&(_ros_message->@(field.name)[i]), nullptr); -@[ else]@ - for (size_t i = 0; i < _ros_message->@(field.name).size; ++i) { - jobject _jlist_@(field.name)_element = _j@(normalized_type)_to_java_function(&(_ros_message->@(field.name).data[i]), nullptr); -@[ end if]@ +@[ if isinstance(member.type, Array)]@ + for (size_t i = 0; i < @(member.type.size); ++i) { + jobject _jlist_@(member.name)_element = _j@(normalized_type)_to_java_function(&(_ros_message->@(member.name)[i]), nullptr); +@[ else]@ + for (size_t i = 0; i < _ros_message->@(member.name).size; ++i) { + jobject _jlist_@(member.name)_element = _j@(normalized_type)_to_java_function(&(_ros_message->@(member.name).data[i]), nullptr); +@[ end if]@ // TODO(esteve): replace ArrayList with a jobjectArray to initialize the array beforehand - jmethodID _jlist_@(field.name)_add_mid = env->GetMethodID(_j@(array_list_normalized_type)_class_global, "add", "(Ljava/lang/Object;)Z"); - if (_jlist_@(field.name)_element != nullptr) { - jboolean _jlist_@(field.name)_add_result = env->CallBooleanMethod(_jarray_list_@(field.name)_obj, _jlist_@(field.name)_add_mid, _jlist_@(field.name)_element); - assert(_jlist_@(field.name)_add_result); + jmethodID _jlist_@(member.name)_add_mid = env->GetMethodID(_j@(array_list_normalized_type)_class_global, "add", "(Ljava/lang/Object;)Z"); + if (_jlist_@(member.name)_element != nullptr) { + jboolean _jlist_@(member.name)_add_result = env->CallBooleanMethod(_jarray_list_@(member.name)_obj, _jlist_@(member.name)_add_mid, _jlist_@(member.name)_element); + assert(_jlist_@(member.name)_add_result); } } -@[ end if]@ - env->SetObjectField(_jmessage_obj, _jfield_@(field.name)_fid, _jarray_list_@(field.name)_obj); - env->DeleteLocalRef(_jarray_list_@(field.name)_obj); -@[ else]@ -@[ if field.type.is_primitive_type()]@ -@[ if field.type.type == 'string']@ - auto _jfield_@(field.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(field.name)", "Ljava/lang/String;"); - if (_ros_message->@(field.name).data != nullptr) { - env->SetObjectField(_jmessage_obj, _jfield_@(field.name)_fid, env->NewStringUTF(_ros_message->@(field.name).data)); +@[ end if]@ + env->SetObjectField(_jmessage_obj, _jfield_@(member.name)_fid, _jarray_list_@(member.name)_obj); + env->DeleteLocalRef(_jarray_list_@(member.name)_obj); +@[ else]@ +@[ if isinstance(member.type, AbstractGenericString)]@ + auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "Ljava/lang/String;"); + if (_ros_message->@(member.name).data != nullptr) { +@[ if isinstance(member.type, AbstractString)]@ + env->SetObjectField(_jmessage_obj, _jfield_@(member.name)_fid, env->NewStringUTF(_ros_message->@(member.name).data)); +@[ else]@ + env->SetObjectField(_jmessage_obj, _jfield_@(member.name)_fid, env->NewString(_ros_message->@(member.name).data, _ros_message->@(member.name).size)); +@[ end if]@ } -@[ else]@ +@[ elif isinstance(member.type, BasicType)]@ @{ -jni_signature = get_jni_signature(field.type) -set_method_name = 'Set%sField' % get_java_type(field.type, use_primitives=True).capitalize() +jni_signature = get_jni_signature(member.type) +set_method_name = 'Set%sField' % get_java_type(member.type, use_primitives=True).capitalize() }@ - auto _jfield_@(field.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(field.name)", "@(jni_signature)"); - env->@(set_method_name)(_jmessage_obj, _jfield_@(field.name)_fid, _ros_message->@(field.name)); -@[ end if]@ -@[ else]@ - auto _jfield_@(field.name)_fid = env->GetFieldID( - _j@(msg_normalized_type)_class_global, "@(field.name)", "L@(field.type.pkg_name)/msg/@(field.type.type);"); - assert(_jfield_@(field.name)_fid != nullptr); + auto _jfield_@(member.name)_fid = env->GetFieldID(_j@(msg_normalized_type)_class_global, "@(member.name)", "@(jni_signature)"); + env->@(set_method_name)(_jmessage_obj, _jfield_@(member.name)_fid, _ros_message->@(member.name)); +@[ else]@ + auto _jfield_@(member.name)_fid = env->GetFieldID( + _j@(msg_normalized_type)_class_global, "@(member.name)", "L@('/'.join(member.type.namespaced_name()));"); + assert(_jfield_@(member.name)_fid != nullptr); - jobject _jfield_@(field.name)_obj = _j@(normalized_type)_to_java_function(&(_ros_message->@(field.name)), nullptr); + jobject _jfield_@(member.name)_obj = _j@(normalized_type)_to_java_function(&(_ros_message->@(member.name)), nullptr); - env->SetObjectField(_jmessage_obj, _jfield_@(field.name)_fid, _jfield_@(field.name)_obj); -@[ end if]@ + env->SetObjectField(_jmessage_obj, _jfield_@(member.name)_fid, _jfield_@(member.name)_obj); @[ end if]@ +@[ end if]@ @[end for]@ assert(_jmessage_obj != nullptr); return _jmessage_obj; @@ -406,7 +393,7 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM * vm, void *) } JNIEnv * env; - if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8) != JNI_OK) { return JNI_ERR; } else { @[for normalized_type, jni_type in cache.items()]@ @@ -415,20 +402,20 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM * vm, void *) _j@(normalized_type)_class_global = static_cast(env->NewGlobalRef(_j@(normalized_type)_class_local)); env->DeleteLocalRef(_j@(normalized_type)_class_local); assert(_j@(normalized_type)_class_global != nullptr); -@[if constructor_signatures[jni_type]]@ +@[ if constructor_signatures[jni_type]]@ _j@(normalized_type)_constructor_global = env->GetMethodID(_j@(normalized_type)_class_global, "", "@(constructor_signatures[jni_type])"); assert(_j@(normalized_type)_constructor_global != nullptr); -@[end if]@ +@[ end if]@ @{ value_method = value_methods.get(jni_type) if value_method: value_method_name, value_method_signature = value_method }@ -@[ if value_method]@ +@[ if value_method]@ _j@(normalized_type)_value_global = env->GetMethodID(_j@(normalized_type)_class_global, "@(value_method_name)", "@(value_method_signature)"); assert(_j@(normalized_type)_value_global != nullptr); -@[ end if]@ -@[ if jni_type in non_primitive_types]@ +@[ end if]@ +@[ if jni_type in namespaced_types]@ _j@(normalized_type)_from_java_converter_global = env->GetStaticMethodID( _j@(normalized_type)_class_global, "getFromJavaConverter", "()J"); assert(_j@(normalized_type)_from_java_converter_global != nullptr); @@ -452,10 +439,10 @@ if value_method: _j@(normalized_type)_to_java_function = reinterpret_cast<_j@(normalized_type)_to_java_signature>(_j@(normalized_type)_to_java_converter_ptr_global); assert(_j@(normalized_type)_to_java_function != nullptr); -@[ end if]@ +@[ end if]@ @[end for]@ } - return JNI_VERSION_1_6; + return JNI_VERSION_1_8; } JNIEXPORT void JNICALL JNI_OnUnload(JavaVM * vm, void *) @@ -464,18 +451,18 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM * vm, void *) assert(g_vm == vm); JNIEnv * env; - if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) == JNI_OK) { + if (g_vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_8) == JNI_OK) { @[for normalized_type, jni_type in cache.items()]@ if (_j@(normalized_type)_class_global != nullptr) { env->DeleteGlobalRef(_j@(normalized_type)_class_global); _j@(normalized_type)_class_global = nullptr; -@[ if constructor_signatures[jni_type]]@ +@[ if constructor_signatures[jni_type]]@ _j@(normalized_type)_constructor_global = nullptr; -@[ end if]@ -@[ if value_methods.get(jni_type)]@ +@[ end if]@ +@[ if value_methods.get(jni_type)]@ _j@(normalized_type)_value_global = nullptr; -@[ end if]@ -@[ if jni_type in non_primitive_types]@ +@[ end if]@ +@[ if jni_type in namespaced_types]@ _j@(normalized_type)_from_java_converter_global = nullptr; _j@(normalized_type)_from_java_converter_ptr_global = 0; _j@(normalized_type)_from_java_function = nullptr; @@ -483,31 +470,31 @@ JNIEXPORT void JNICALL JNI_OnUnload(JavaVM * vm, void *) _j@(normalized_type)_to_java_converter_global = nullptr; _j@(normalized_type)_to_java_converter_ptr_global = 0; _j@(normalized_type)_to_java_function = nullptr; -@[ end if]@ +@[ end if]@ } @[end for]@ } } -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getFromJavaConverter(JNIEnv *, jclass) +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getFromJavaConverter(JNIEnv *, jclass) { - jlong ptr = reinterpret_cast(&@(spec.base_type.pkg_name)_@(type_name)__convert_from_java); + jlong ptr = reinterpret_cast(&@(underscore_separated_type_name)__convert_from_java); return ptr; } -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getToJavaConverter(JNIEnv *, jclass) +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getToJavaConverter(JNIEnv *, jclass) { - jlong ptr = reinterpret_cast(@(spec.base_type.pkg_name)_@(type_name)__convert_to_java); + jlong ptr = reinterpret_cast(@(underscore_separated_type_name)__convert_to_java); return ptr; } -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getTypeSupport(JNIEnv *, jclass) +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getTypeSupport(JNIEnv *, jclass) { - jlong ptr = reinterpret_cast(ROSIDL_GET_MSG_TYPE_SUPPORT(@(spec.base_type.pkg_name), @(subfolder), @(spec.msg_name))); + jlong ptr = reinterpret_cast(ROSIDL_GET_MSG_TYPE_SUPPORT(@(','.join(message.structure.namespaced_type.namespaced_name())))); return ptr; } -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getDestructor(JNIEnv *, jclass) +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getDestructor(JNIEnv *, jclass) { jlong ptr = reinterpret_cast(@(msg_normalized_type)__destroy); return ptr; diff --git a/rosidl_generator_java/resource/msg.java.em b/rosidl_generator_java/resource/msg.java.em index 4409c1f3..cb6e1b3a 100644 --- a/rosidl_generator_java/resource/msg.java.em +++ b/rosidl_generator_java/resource/msg.java.em @@ -1,18 +1,41 @@ -package @(package_name).@(subfolder); - -import org.ros2.rcljava.common.JNIUtils; -import org.ros2.rcljava.interfaces.MessageDefinition; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import org.apache.commons.lang3.builder.EqualsBuilder; -import org.apache.commons.lang3.builder.HashCodeBuilder; +@# Generation triggered from rosidl_generator_java/resource/idl.java.em +// generated from rosidl_generator_java/resource/msg.java.em +// with input from @(package_name):@(interface_path) +// generated code does not contain a copyright notice + +package @(package_name + '.' + interface_path.parts[0]); +@{ +from rosidl_generator_java import convert_lower_case_underscore_to_camel_case +from rosidl_generator_java import get_java_type +from rosidl_generator_java import primitive_value_to_java +from rosidl_generator_java import value_to_java +from rosidl_parser.definition import AbstractGenericString +from rosidl_parser.definition import AbstractNestedType +from rosidl_parser.definition import Array +from rosidl_parser.definition import BasicType +from rosidl_parser.definition import BoundedSequence +from rosidl_parser.definition import NamespacedType + +type_name = message.structure.namespaced_type.name + +message_imports = [ + 'org.apache.commons.lang3.builder.EqualsBuilder', + 'org.apache.commons.lang3.builder.HashCodeBuilder', + 'org.ros2.rcljava.common.JNIUtils', + 'org.ros2.rcljava.interfaces.MessageDefinition', + 'org.slf4j.Logger', + 'org.slf4j.LoggerFactory', +] +}@ +@[for message_import in message_imports]@ +import @(message_import); +@[end for]@ -@[for field in spec.fields]@ -@[ if not field.type.is_primitive_type()]@ -import @(field.type.pkg_name).msg.@(field.type.type); -@[ end if]@ +@[for member in message.structure.members]@ +@[ if isinstance(member.type, NamespacedType)]@ +// Member '@(member.name)' +import @('.'.join(member.type.namespaced_name())); +@[ end if]@ @[end for]@ public final class @(type_name) implements MessageDefinition { @@ -49,85 +72,84 @@ public final class @(type_name) implements MessageDefinition { return @(type_name).getTypeSupport(); } -@[for constant in spec.constants]@ - public static final @(get_builtin_java_type(constant.type)) @(constant.name) = @(constant_value_to_java(constant.type, constant.value)); +@[for constant in message.constants]@ + public static final @(get_java_type(constant.type)) @(constant.name) = @(primitive_value_to_java(constant.type, constant.value)); @[end for]@ -@[for field in spec.fields]@ - -@[ if field.type.is_array]@ -@[ if field.default_value is not None]@ - private java.util.List<@(get_java_type(field.type, use_primitives=False))> @(field.name) = java.util.Arrays.asList(new @(get_java_type(field.type, use_primitives=False))[] @(value_to_java(field.type, field.default_value))); -@[ else]@ -@[ if field.type.array_size]@ - private java.util.List<@(get_java_type(field.type, use_primitives=False))> @(field.name); -@[ else]@ - private java.util.List<@(get_java_type(field.type, use_primitives=False))> @(field.name) = new java.util.ArrayList<@(get_java_type(field.type, use_primitives=False))>(); -@[ end if]@ -@[ end if]@ - - public final @(type_name) set@(convert_lower_case_underscore_to_camel_case(field.name))(final java.util.List<@(get_java_type(field.type, use_primitives=False))> @(field.name)) { -@[ if field.type.array_size]@ -@[ if field.type.is_upper_bound]@ - if(@(field.name).size() > @(field.type.array_size)) { - throw new IllegalArgumentException("List too big, maximum size allowed: @(field.type.array_size)"); -@[ else]@ - if(@(field.name).size() != @(field.type.array_size)) { - throw new IllegalArgumentException("Invalid size for fixed array, must be exactly: @(field.type.array_size)"); -@[ end if]@ +@[for member in message.structure.members]@ + +@[ if isinstance(member.type, AbstractNestedType)]@ +@[ if member.has_annotation('default')]@ + private java.util.List<@(get_java_type(member.type, use_primitives=False))> @(member.name) = java.util.Arrays.asList(new @(get_java_type(member.type, use_primitives=False))[] @(value_to_java(member.type, member.get_annotation_value('default')['value']))); +@[ else]@ +@[ if isinstance(member.type, Array)]@ + private java.util.List<@(get_java_type(member.type, use_primitives=False))> @(member.name); +@[ else]@ + private java.util.List<@(get_java_type(member.type, use_primitives=False))> @(member.name) = new java.util.ArrayList<@(get_java_type(member.type, use_primitives=False))>(); +@[ end if]@ +@[ end if]@ + + public final @(type_name) set@(convert_lower_case_underscore_to_camel_case(member.name))(final java.util.List<@(get_java_type(member.type, use_primitives=False))> @(member.name)) { +@[ if isinstance(member.type, BoundedSequence)]@ + if(@(member.name).size() > @(member.type.maximum_size)) { + throw new IllegalArgumentException("List too big, maximum size allowed: @(member.type.maximum_size)"); + } +@[ elif isinstance(member.type, Array)]@ + if(@(member.name).size() != @(member.type.size)) { + throw new IllegalArgumentException("Invalid size for fixed array, must be exactly: @(member.type.size)"); } -@[ end if]@ - this.@(field.name) = @(field.name); +@[ end if]@ + this.@(member.name) = @(member.name); return this; } -@[ if field.type.is_primitive_type()]@ - public final @(type_name) set@(convert_lower_case_underscore_to_camel_case(field.name))(final @(get_java_type(field.type, use_primitives=True))[] @(field.name)) { - java.util.List<@(get_java_type(field.type, use_primitives=False))> @(field.name)_tmp = new java.util.ArrayList<@(get_java_type(field.type, use_primitives=False))>(); - for(@(get_java_type(field.type, use_primitives=True)) @(field.name)_value : @(field.name)) { - @(field.name)_tmp.add(@(field.name)_value); +@[ if isinstance(member.type.value_type, (BasicType, AbstractGenericString))]@ + public final @(type_name) set@(convert_lower_case_underscore_to_camel_case(member.name))(final @(get_java_type(member.type, use_primitives=True))[] @(member.name)) { + java.util.List<@(get_java_type(member.type, use_primitives=False))> @(member.name)_tmp = new java.util.ArrayList<@(get_java_type(member.type, use_primitives=False))>(); + for(@(get_java_type(member.type, use_primitives=True)) @(member.name)_value : @(member.name)) { + @(member.name)_tmp.add(@(member.name)_value); } - return set@(convert_lower_case_underscore_to_camel_case(field.name))(@(field.name)_tmp); + return set@(convert_lower_case_underscore_to_camel_case(member.name))(@(member.name)_tmp); } -@[ end if]@ +@[ end if]@ - public final java.util.List<@(get_java_type(field.type, use_primitives=False))> get@(convert_lower_case_underscore_to_camel_case(field.name))() { - return this.@(field.name); + public final java.util.List<@(get_java_type(member.type, use_primitives=False))> get@(convert_lower_case_underscore_to_camel_case(member.name))() { + return this.@(member.name); } +@[ else]@ +@[ if member.has_annotation('default')]@ + private @(get_java_type(member.type)) @(member.name) = @(value_to_java(member.type, member.get_annotation_value('default')['value'])); @[ else]@ -@[ if field.default_value is not None]@ - private @(get_java_type(field.type)) @(field.name) = @(value_to_java(field.type, field.default_value)); -@[ else]@ -@[ if field.type.type == 'string']@ - private @(get_java_type(field.type)) @(field.name) = ""; -@[ elif field.type.is_primitive_type()]@ - private @(get_java_type(field.type)) @(field.name); -@[ else]@ - private @(get_java_type(field.type)) @(field.name) = new @(get_java_type(field.type))(); -@[ end if]@ -@[ end if]@ - - public @(type_name) set@(convert_lower_case_underscore_to_camel_case(field.name))(final @(get_java_type(field.type)) @(field.name)) { -@[ if field.type.string_upper_bound]@ - if(@(field.name).length() > @(field.type.string_upper_bound)) { - throw new IllegalArgumentException("String too long, maximum size allowed: @(field.type.string_upper_bound)"); +@[ if isinstance(member.type, AbstractGenericString)]@ + private @(get_java_type(member.type)) @(member.name) = ""; +@[ elif isinstance(member.type, BasicType)]@ + private @(get_java_type(member.type)) @(member.name); +@[ else]@ + private @(get_java_type(member.type)) @(member.name) = new @(get_java_type(member.type))(); +@[ end if]@ +@[ end if]@ + + public @(type_name) set@(convert_lower_case_underscore_to_camel_case(member.name))(final @(get_java_type(member.type)) @(member.name)) { +@[ if isinstance(member.type, AbstractGenericString) and member.type.has_maximum_size()]@ + if(@(member.name).length() > @(member.type.maximum_size)) { + throw new IllegalArgumentException("String too long, maximum size allowed: @(member.type.maximum_size)"); } -@[ end if]@ +@[ end if]@ - this.@(field.name) = @(field.name); + this.@(member.name) = @(member.name); return this; } - public @(get_java_type(field.type)) get@(convert_lower_case_underscore_to_camel_case(field.name))() { - return this.@(field.name); + public @(get_java_type(member.type)) get@(convert_lower_case_underscore_to_camel_case(member.name))() { + return this.@(member.name); } -@[ end if]@ +@[ end if]@ @[end for]@ public int hashCode() { return new HashCodeBuilder(17, 37) -@[for field in spec.fields]@ - .append(this.@(field.name)) +@[for member in message.structure.members]@ + .append(this.@(member.name)) @[end for]@ .toHashCode(); } @@ -140,8 +162,8 @@ public final class @(type_name) implements MessageDefinition { } @(type_name) rhs = (@(type_name)) obj; return new EqualsBuilder() -@[for field in spec.fields]@ - .append(this.@(field.name), rhs.@(field.name)) +@[for member in message.structure.members]@ + .append(this.@(member.name), rhs.@(member.name)) @[end for]@ .isEquals(); } diff --git a/rosidl_generator_java/resource/srv.cpp.em b/rosidl_generator_java/resource/srv.cpp.em index 71014762..5c2d2d0b 100644 --- a/rosidl_generator_java/resource/srv.cpp.em +++ b/rosidl_generator_java/resource/srv.cpp.em @@ -1,29 +1,48 @@ -// generated from rosidl_generator_java/resource/srv.cpp.em -// generated code does not contain a copyright notice +@# Included from rosidl_generator_java/resource/idl.cpp.em +@{ +from rosidl_generator_c import idl_structure_type_to_c_include_prefix -#include +service_includes = [ + 'rosidl_generator_c/service_type_support_struct.h', +] +}@ +@[for include in service_includes]@ +@[ if include in include_directives]@ +// already included above +// @ +@[ else]@ +@{include_directives.add(include)}@ +@[ end if]@ +#include "@(include)" +@[end for]@ -#include "rosidl_generator_c/service_type_support_struct.h" -#include "@(spec.pkg_name)/@(subfolder)/@(module_name).h" +#include "@(idl_structure_type_to_c_include_prefix(service.namespaced_type)).h" #ifdef __cplusplus extern "C" { #endif +@{ +from rosidl_generator_java import get_jni_mangled_name + +service_fqn = service.namespaced_type.namespaced_name() +underscore_separated_type_name = '_'.join(service_fqn) +underscore_separated_jni_type_name = get_jni_mangled_name(service_fqn) +}@ /* - * Class: @(jni_package_name)_@(subfolder)_@(type_name) + * Class: @(underscore_separated_type_name) * Method: getServiceTypeSupport * Signature: ()J */ -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getServiceTypeSupport(JNIEnv *, jclass); +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getServiceTypeSupport(JNIEnv *, jclass); #ifdef __cplusplus } #endif -JNIEXPORT jlong JNICALL Java_@(jni_package_name)_@(subfolder)_@(jni_type_name)_getServiceTypeSupport(JNIEnv *, jclass) +JNIEXPORT jlong JNICALL Java_@(underscore_separated_jni_type_name)_getServiceTypeSupport(JNIEnv *, jclass) { const rosidl_service_type_support_t * ts = ROSIDL_GET_SRV_TYPE_SUPPORT( - @(spec.pkg_name), @(spec.srv_name)); + @(','.join(service_fqn))); return reinterpret_cast(ts); } diff --git a/rosidl_generator_java/resource/srv.java.em b/rosidl_generator_java/resource/srv.java.em index d7e992f4..9be7a6a7 100644 --- a/rosidl_generator_java/resource/srv.java.em +++ b/rosidl_generator_java/resource/srv.java.em @@ -1,10 +1,51 @@ -package @(package_name).@(subfolder); +@# Generation triggered from rosidl_generator_java/resource/idl.java.em +// generated from rosidl_generator_java/resource/srv.java.em +// with input from @(package_name):@(interface_path) +// generated code does not contain a copyright notice -import org.ros2.rcljava.common.JNIUtils; -import org.ros2.rcljava.interfaces.ServiceDefinition; +package @(package_name + '.' + interface_path.parts[0]); +@{ +import os +from rosidl_cmake import expand_template -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +namespaces = service.namespaced_type.namespaces +type_name = service.namespaced_type.name +request_type_name = service.request_message.structure.namespaced_type.name +response_type_name = service.response_message.structure.namespaced_type.name + +data = { + 'package_name': package_name, + 'interface_path': interface_path, + 'output_dir': output_dir, + 'template_basepath': template_basepath, +} +data.update({'message': service.request_message}) +output_file = os.path.join(output_dir, *namespaces[1:], request_type_name + '.java') +expand_template( + 'msg.java.em', + data, + output_file, + template_basepath=template_basepath) + +data.update({'message': service.response_message}) +output_file = os.path.join(output_dir, *namespaces[1:], response_type_name + '.java') +expand_template( + 'msg.java.em', + data, + output_file, + template_basepath=template_basepath) + +service_imports = [ + 'org.ros2.rcljava.common.JNIUtils', + 'org.ros2.rcljava.interfaces.ServiceDefinition', + 'org.slf4j.Logger', + 'org.slf4j.LoggerFactory', +] +}@ + +@[for service_import in service_imports]@ +import @(service_import); +@[end for]@ public class @(type_name) implements ServiceDefinition { diff --git a/rosidl_generator_java/rosidl_generator_java/__init__.py b/rosidl_generator_java/rosidl_generator_java/__init__.py index 8d9163f3..7941ab69 100644 --- a/rosidl_generator_java/rosidl_generator_java/__init__.py +++ b/rosidl_generator_java/rosidl_generator_java/__init__.py @@ -1,4 +1,5 @@ # Copyright 2016-2017 Esteve Fernandez +# Copyright 2019 Open Source Robotics Foundation, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -12,15 +13,17 @@ # See the License for the specific language governing permissions and # limitations under the License. +from ast import literal_eval from collections import defaultdict -import os +import pathlib -from rosidl_cmake import convert_camel_case_to_lower_case_underscore -from rosidl_cmake import expand_template -from rosidl_cmake import get_newest_modification_time +from rosidl_cmake import generate_files from rosidl_cmake import read_generator_arguments -from rosidl_parser import parse_message_file -from rosidl_parser import parse_service_file +from rosidl_parser.definition import AbstractGenericString +from rosidl_parser.definition import AbstractNestedType +from rosidl_parser.definition import BASIC_TYPES +from rosidl_parser.definition import BasicType +from rosidl_parser.definition import NamespacedType # Taken from http://stackoverflow.com/a/6425628 @@ -28,201 +31,176 @@ def convert_lower_case_underscore_to_camel_case(word): return ''.join(x.capitalize() or '_' for x in word.split('_')) -def generate_java(generator_arguments_file, typesupport_impl, typesupport_impls): +def generate_java(generator_arguments_file, typesupport_impls): args = read_generator_arguments(generator_arguments_file) - typesupport_impls = typesupport_impls.split(';') - - template_dir = args['template_dir'] - type_support_impl_by_filename = { - '%s.ep.{0}.cpp'.format(impl): impl - for impl in typesupport_impls - } - mapping_msgs = { - os.path.join(template_dir, 'msg.java.em'): ['%s.java'], - os.path.join(template_dir, 'msg.cpp.em'): type_support_impl_by_filename.keys(), + additional_context = { + 'output_dir': pathlib.Path(args['output_dir']), + 'template_basepath': pathlib.Path(args['template_dir']), } - - mapping_srvs = { - os.path.join(template_dir, 'srv.java.em'): ['%s.java'], - os.path.join(template_dir, 'srv.cpp.em'): type_support_impl_by_filename.keys(), + mapping = { + 'idl.java.em': '_%s.java', } - - for template_file in mapping_msgs.keys(): - assert os.path.exists(template_file), \ - 'Messages template file %s not found' % template_file - for template_file in mapping_srvs.keys(): - assert os.path.exists(template_file), \ - 'Services template file %s not found' % template_file - - functions = {'get_java_type': get_java_type, } - latest_target_timestamp = get_newest_modification_time(args['target_dependencies']) - - modules = defaultdict(list) - for ros_interface_file in args['ros_interface_files']: - extension = os.path.splitext(ros_interface_file)[1] - subfolder = os.path.basename(os.path.dirname(ros_interface_file)) - if extension == '.msg': - spec = parse_message_file(args['package_name'], ros_interface_file) - mapping = mapping_msgs - type_name = spec.base_type.type - elif extension == '.srv': - spec = parse_service_file(args['package_name'], ros_interface_file) - mapping = mapping_srvs - type_name = spec.srv_name - else: - continue - - module_name = convert_camel_case_to_lower_case_underscore(type_name) - modules[subfolder].append((module_name, type_name)) - package_name = args['package_name'] - jni_package_name = package_name.replace('_', '_1') - jni_type_name = type_name.replace('_', '_1') - for template_file, generated_filenames in mapping.items(): - for generated_filename in generated_filenames: - data = { - 'constant_value_to_java': constant_value_to_java, - 'value_to_java': value_to_java, - 'convert_camel_case_to_lower_case_underscore': - convert_camel_case_to_lower_case_underscore, - 'convert_lower_case_underscore_to_camel_case': - convert_lower_case_underscore_to_camel_case, - 'get_builtin_java_type': get_builtin_java_type, - 'module_name': module_name, - 'package_name': package_name, - 'jni_package_name': jni_package_name, - 'jni_type_name': jni_type_name, - 'spec': spec, - 'subfolder': subfolder, - 'typesupport_impl': type_support_impl_by_filename.get(generated_filename, ''), - 'typesupport_impls': typesupport_impls, - 'type_name': type_name, - } - data.update(functions) - generated_file = os.path.join(args['output_dir'], subfolder, - generated_filename % type_name) - expand_template( - template_file, data, generated_file, minimum_timestamp=latest_target_timestamp) - + generate_files( + generator_arguments_file, mapping, additional_context=additional_context, keep_case=True) + + for impl in typesupport_impls: + mapping = { + 'idl.cpp.em': '%s.ep.{0}.cpp'.format(impl), + } + generate_files( + generator_arguments_file, + mapping, + additional_context=additional_context, + keep_case=True) return 0 def escape_string(s): s = s.replace('\\', '\\\\') - s = s.replace("'", "\\'") + s = s.replace('"', '\\"') return s +def escape_wstring(s): + return escape_string(s) + + def value_to_java(type_, value): - assert type_.is_primitive_type() - assert value is not None + assert not isinstance(type_, NamespacedType), \ + "Could not convert non-basic type '{}' to Java".format(type_) + assert value is not None, "Value for for type '{}' must not be None".format(type_) - if not type_.is_array: + if not isinstance(type_, AbstractNestedType): return primitive_value_to_java(type_, value) java_values = [] - for single_value in value: - java_value = primitive_value_to_java(type_, single_value) + for single_value in literal_eval(value): + java_value = primitive_value_to_java(type_.value_type, single_value) java_values.append(java_value) return '{%s}' % ', '.join(java_values) def primitive_value_to_java(type_, value): - assert type_.is_primitive_type() - assert value is not None + assert isinstance(type_, (BasicType, AbstractGenericString)), \ + "Could not convert non-basic type '{}' to Java".format(type_) + assert value is not None, "Value for for type '{}' must not be None".format(type_) - if type_.type == 'bool': - return 'true' if value else 'false' - - if type_.type in [ - 'byte', - 'char', - 'int8', - 'uint8', - 'int16', - 'uint16', - 'int32', - 'uint32', - 'int64', - 'uint64', - 'float64', - ]: - return str(value) - - if type_.type == 'float32': - return '%sf' % value - - if type_.type == 'string': + if isinstance(type_, AbstractGenericString): return '"%s"' % escape_string(value) - assert False, "unknown primitive type '%s'" % type_ - - -def constant_value_to_java(type_, value): - assert value is not None - - if type_ == 'bool': + if type_.typename == 'boolean': return 'true' if value else 'false' - if type_ in [ - 'byte', - 'char', - 'int8', - 'uint8', - 'int16', - 'uint16', - 'int32', - 'uint32', - 'int64', - 'uint64', - 'float64', - ]: - return str(value) - - if type_ == 'float32': + if type_.typename == 'float': return '%sf' % value - if type_ == 'string': - return '"%s"' % escape_string(value) - - assert False, "unknown constant type '%s'" % type_ - + if type_.typename in ('octet', 'uint8', 'int8'): + # literal is treated as an integer so we must cast + return '(byte) %s' % value -def get_builtin_java_type(type_, use_primitives=True): - if type_ == 'bool': - return 'boolean' if use_primitives else 'java.lang.Boolean' + if type_.typename in ('uint16', 'int16'): + # literal is treated as an integer so we must cast + return '(short) %s' % value - if type_ == 'byte': - return 'byte' if use_primitives else 'java.lang.Byte' + # Java doesn't support unsigned literals (values over 2^31-1) + # Instead we should convert to the corresponding negative number + if type_.typename == 'uint32' and int(value) > 2**31-1: + negative_value = int(value) - 2**32 + return str(negative_value) - if type_ == 'char': - return 'char' if use_primitives else 'java.lang.Character' + if type_.typename in ('uint64', 'int64'): + # Java doesn't support unsigned literals (values over 2^63-1) + # Instead we should convert to the corresponding negative number + if type_.typename == 'uint64' and int(value) > 2**63-1: + value = int(value) - 2**64 + return '%sL' % value - if type_ == 'float32': - return 'float' if use_primitives else 'java.lang.Float' - - if type_ == 'float64': - return 'double' if use_primitives else 'java.lang.Double' - - if type_ in ['int8', 'uint8']: - return 'byte' if use_primitives else 'java.lang.Byte' - - if type_ in ['int16', 'uint16']: - return 'short' if use_primitives else 'java.lang.Short' - - if type_ in ['int32', 'uint32']: - return 'int' if use_primitives else 'java.lang.Integer' - - if type_ in ['int64', 'uint64']: - return 'long' if use_primitives else 'java.lang.Long' + if type_.typename in BASIC_TYPES: + return str(value) - if type_ == 'string': + assert False, "unknown primitive type '%s'" % type_.typename + + +# Map IDL types to Java primitive types +# Maps to a tuple: (primitive, class type) +IDL_TYPE_TO_JAVA_PRIMITIVE = { + 'boolean': ('boolean', 'java.lang.Boolean'), + 'char': ('char', 'java.lang.Char'), + 'octet': ('byte', 'java.lang.Byte'), + 'float': ('float', 'java.lang.Float'), + 'double': ('double', 'java.lang.Double'), + 'long double': ('double', 'java.lang.Double'), + 'uint8': ('byte', 'java.lang.Byte'), + 'int8': ('byte', 'java.lang.Byte'), + 'uint16': ('short', 'java.lang.Short'), + 'int16': ('short', 'java.lang.Short'), + 'uint32': ('int', 'java.lang.Integer'), + 'int32': ('int', 'java.lang.Integer'), + 'uint64': ('long', 'java.lang.Long'), + 'int64': ('long', 'java.lang.Long'), +} + + +def get_java_type(type_, use_primitives=True): + if isinstance(type_, AbstractNestedType): + type_ = type_.value_type + if isinstance(type_, NamespacedType): + return '.'.join(type_.namespaced_name()) + if isinstance(type_, BasicType): + return IDL_TYPE_TO_JAVA_PRIMITIVE[type_.typename][0 if use_primitives else 1] + if isinstance(type_, AbstractGenericString): return 'java.lang.String' assert False, "unknown type '%s'" % type_ -def get_java_type(type_, use_primitives=True, subfolder='msg'): - if not type_.is_primitive_type(): - return '%s.%s.%s' % (type_.pkg_name, subfolder, type_.type) - - return get_builtin_java_type(type_.type, use_primitives=use_primitives) +def get_normalized_type(type_): + return get_java_type(type_, use_primitives=False).replace('.', '__') + + +def get_jni_type(type_): + return get_java_type(type_, use_primitives=False).replace('.', '/') + + +# JNI performance tips taken from http://planet.jboss.org/post/jni_performance_the_saga_continues +constructor_signatures = defaultdict(lambda: '()V') +constructor_signatures['java/lang/Boolean'] = '(Z)V' +constructor_signatures['java/lang/Byte'] = '(B)V' +constructor_signatures['java/lang/Character'] = '(C)V' +constructor_signatures['java/lang/Double'] = '(D)V' +constructor_signatures['java/lang/Float'] = '(F)V' +constructor_signatures['java/lang/Integer'] = '(I)V' +constructor_signatures['java/lang/Long'] = '(J)V' +constructor_signatures['java/lang/Short'] = '(S)V' +constructor_signatures['java/util/List'] = None + +value_methods = {} +value_methods['java/lang/Boolean'] = ('booleanValue', '()Z') +value_methods['java/lang/Byte'] = ('byteValue', '()B') +value_methods['java/lang/Character'] = ('charValue', '()C') +value_methods['java/lang/Double'] = ('doubleValue', '()D') +value_methods['java/lang/Float'] = ('floatValue', '()F') +value_methods['java/lang/Integer'] = ('intValue', '()I') +value_methods['java/lang/Long'] = ('longValue', '()J') +value_methods['java/lang/Short'] = ('shortValue', '()S') + +jni_signatures = {} +jni_signatures['java/lang/Boolean'] = 'Z' +jni_signatures['java/lang/Byte'] = 'B' +jni_signatures['java/lang/Character'] = 'C' +jni_signatures['java/lang/Double'] = 'D' +jni_signatures['java/lang/Float'] = 'F' +jni_signatures['java/lang/Integer'] = 'I' +jni_signatures['java/lang/Long'] = 'J' +jni_signatures['java/lang/Short'] = 'S' + + +def get_jni_signature(type_): + global jni_signatures + return jni_signatures.get(get_jni_type(type_)) + + +def get_jni_mangled_name(fully_qualified_name): + # JNI name mangling: + # https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names + return fully_qualified_name[0].replace('_', '_1') + '_' + '_'.join(fully_qualified_name[1:]) diff --git a/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java b/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java index 03a5011d..d1b0fada 100644 --- a/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java +++ b/rosidl_generator_java/src/test/java/org/ros2/generator/InterfacesTest.java @@ -68,12 +68,12 @@ public final void testByte() { @Test public final void testChar() { - char expected1 = 'a'; + byte expected1 = 'a'; rosidl_generator_java.msg.Char charOne = new rosidl_generator_java.msg.Char(); charOne.setEmptyChar(expected1); assertEquals(expected1, charOne.getEmptyChar()); - char expected2 = 'b'; + byte expected2 = 'b'; rosidl_generator_java.msg.Char charTwo = new rosidl_generator_java.msg.Char(); charTwo.setEmptyChar(expected2); assertEquals(expected2, charTwo.getEmptyChar());